【问题标题】:Global variable and optional binding in SwiftSwift 中的全局变量和可选绑定
【发布时间】:2015-12-13 02:04:31
【问题描述】:

我对可选绑定、全局变量和包装和展开有一些非常简单的疑问。由于我是 SWIFT 的新手,因此了解他们的概念非常重要。

1) 在 Swift 中,如果我声明一个全局变量,我有 2 个选项可以使其成为可选或非可选,所以让我拥有 2-4 个或更多可选变量。那么在

中可选绑定所有这些变量是否可取?
viewDidLoad() method// so that I could use them without any problem of unwrapping and fatal error in my program.

2) 让我通过以下示例让自己更清楚——我的项目 VC1 和 VC2 中有 2 个 VC。 VC2 有一个文本字段,用户可以在其中输入一些值并将其显示在 VC1 的表格视图中。

在VC1中

var namevc1 = NSMutableArray?//holds the input of textfield to be passed from VC2. 

如您所见,我的 VC1 是我的项目运行时加载的第一个视图控制器,我使用一个可选变量来填充我的 tabke vuew,即

'arr'

所以当应用程序第一次运行时它是空的。因此在代码中使用它的值时可能会导致致命错误。那么在

中是否解除绑定有什么解决办法
viewDidLoad() 

方法或全部声明一个空的 NSMutable 数组类型来代替可选类型。

提前致谢。

【问题讨论】:

  • 您应该避免使用“全局变量”。对于控制器之间的通信,请使用委托。
  • @dfri 如果我想通过 prepareForSegue(),我显然会选择全局变量。
  • 我认为你误解了 Swift 中全局变量的概念。 1. 如果您有一个全局变量,则不必在任何视图/方法/类等之间“传递”它,因为该变量是在全局范围内定义的(可在任何地方访问)。 2. 通常全局变量不是一个好主意,并且是你想要避免的。参见例如stackoverflow.com/questions/26195262/…stackoverflow.com/questions/26601163/…
  • @dfri 抱歉迟到了。

标签: swift2 ios9 optional-variables


【解决方案1】:

我将从上面重复我的评论开始。

您可能误解了 Swift 中全局变量的概念。

  1. 如果您有一个全局变量,则不必在任何视图/方法/类等之间“传递”它,因为该变量是在全局范围内定义的(可在任何地方访问)。

  2. 一般来说,全局变量不是一个好主意,你想避免这种情况。

关于全局变量和 swift 的问题,您确实应该将 singletons 纳入讨论。参见例如以下现有 SO 线程:


TableViewController 和 ViewController 之间通过 segue 进行通信(准备和展开 segue)

(这个答案最终非常彻底,可能有点过于彻底,因为我不知道您当前的 tableview/viewcontroller 程序状态的详细信息。对于冗长的答案和可能带来的任何不便,我们深表歉意给它的读者)

现在,让我们保留全局变量并讨论您示例中两个控制器之间通信的一个(以及其他)可行选项。根据您的问题,我将您的示例总结如下

  • VC1:故事板入口点,一个由UITableViewCells 组成的UITableViewController,在这些单元格中,您可以通过UILabel 的实例显示一些文本.
  • VC2UIViewController,可从 VC1 的单元格访问,包含一个 UITextField 实例。当用户在此文本字段中输入文本时,您希望文本显示在 VC2 中关联的 单元格 中(关联是指 VC1 中用于访问 VC2 的单元格)。

我们将分别将 VC1 和 VC2 与(可可触摸)类 TableViewController (TableViewController.swift) 和 ViewController (ViewController.swift) 相关联。表格视图控制器中的单元格将与 (cocoa touch) 类 TableViewCell (TableViewCell.swift) 相关联。这些课程的详细信息如下。

对于这个简单的例子,请注意我们不会将 VC1 嵌入到导航控制器中(否则它适用于表格视图 -> 视图导航)。

我们将从情节提要开始,为Table View ControllerView Controller 添加对象(从对象库中拖放)。表格视图容器还将自动在其Table View 中包含TableViewCell。继续故事板:

  • UILabel 对象添加到Table View Controller 中的TableViewCell 容器(根据需要对齐)
  • View Controller 中,添加Text Field 对象和Button 对象(根据需要对齐)。
  • 将入口点设置为Table View Controller
  • 然后按住 Ctrl 键将“显示”转场从 TableViewCell 拖到 View Controller
  • 选择Show segue,然后从属性检查器中输入一个标识符,例如,ShowDetail
  • 最后,选择TableViewCell(如上;来自属性检查器),输入单元格的标识符。在这里,我们将使用简单的标识符 TableViewCell

我们现在暂时离开情节提要并实现三个类,与Table View ControllerView Controller 和前者的TableViewCell 相关联。

我们从Table View Controller 开始,并实现我们的UITableViewController 子类。请注意,这里不是使用NSMutableArray 来保存每个单元格中UITextLabel 的文本,而是使用String 数组。

// TableViewController.swift
Import UIKit

class TableViewController: UITableViewController {

    // Properties
    var userTextLabels = [String]()
    var numberOfCells: Int?

    override func viewDidLoad() {
        super.viewDidLoad()

        numberOfCells = loadSampleTextLabels() // Load sample labels.
    }

    func loadSampleTextLabels() -> Int {
        userTextLabels += ["Label #1", "Label #2", "Label #3"]
        return userTextLabels.count
    }

    // func numberOfSectionsInTableView(tableView: UITableView) ...
    // func tableView(tableView: UITableView, numberOfRowsInSection section: Int) ...

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cellIdentifier = ("TableViewCell")
        let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TableViewCell

        // Text for current cell
        let cellText = userTextLabels[indexPath.row]
        cell.userSuppliedTextLabel.text = cellText

        return cell
    }

    // ... communication?
}

这两个被注释掉的方法是在任何UITableViewController 中使用的标准方法,分别用于表中的部分(例如return 1)和单元格(例如return (numberOfCells ?? 0))的数量。我会把这些问题交给你解决。

现在,我们将表视图中的TableViewCell 对象与UITableViewCell 的子类实例相关联。在这里,我们将为我们的单元格使用一个非常简单的类;每个单元格只包含一个 UILabel 实例(通过故事板 Ctrl 拖动创建为表格视图单元格中的 UILabel 中的 @IBOutlet)。

// TableViewCell.swift
import UIKit

class TableViewCell: UITableViewCell {

    // Properties
    @IBOutlet weak var userSuppliedTextLabel: UILabel!
        // Ctrl-drag from UILabel (in TableViewCell) in storyboard

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }

}

最后,对于从表格视图单元格访问的视图控制器:使用单个@IBOutlet 到用于用户文本输入的UITextField,并使用预先存在的UITextFieldDelegate 处理此文本字段中的事件.例如:

//  ViewController.swift
import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    // Properties
    @IBOutlet weak var userSuppliedText: UITextField!
        // Ctrl-drag from storyboard...
    var cellText: String?

    override func viewDidLoad() {
        super.viewDidLoad()

        userSuppliedText.text = cellText ?? "..."

        // Handle the user input in the text field through delegate callbacks
        userSuppliedText.delegate = self
    }

    // UITextFieldDelegate
    func textFieldShouldReturn(textField: UITextField) -> Bool {
        // User finished typing (hit return): hide the keyboard.
        textField.resignFirstResponder()
        return true
    }

    func textFieldDidEndEditing(textField: UITextField) {
        cellText = textField.text
    }

}

我们还在此处声明了一个字符串属性 (cellText),它将充当 VC1 和 VC2 之间通信的容器。

我们返回到故事板,并从身份检查器中将三个故事板对象(Table View ControllerView ControllerTableViewCell)与我们刚刚在上面编写的相关类关联起来。

我们现在几乎达到了我们的目标;剩下的只是指定两个控制器之间如何通信。

我们将从 VC1 VC2 的通信开始。在您上面的评论中,通过查看prepareForSegue(...) 方法,您走在了正确的轨道上(无论如何,对于这个特定的解决方案)。在Table View Controller 的类中,我们添加以下方法:

// ... add to TableViewController.swift
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.

    if segue.identifier == "ShowDetail" {
        let viewController = segue.destinationViewController as! ViewController
        if let selectedCell = sender as? TableViewCell {
            let indexPath = tableView.indexPathForCell(selectedCell)!
            let currentTextInCell = userTextLabels[indexPath.row]
            viewController.cellText = currentTextInCell // <-- note this
        }
    }
}

因此,对于 VC1->VC2 通信,我们可以(在此示例中)将当前占用 UILabel 的任何现有文本带入发送方单元格(由字符串数组 userTextLabels 指定)。查看 ViewController.swift 中的 viewDidLoad(...) 方法,了解该值是如何从 VC1 传递的,并在 VC2 中的 UITextField 中设置为默认文本。

现在,对于通信 VC2->VC1,这是您询问的具体通信方向,再次添加另一个方法(以编程方式)到 TableViewController.swift

// ... add to TableViewController.swift
@IBAction func unwindToTableView(sender: UIStoryboardSegue) {
    if let sourceViewController = sender.sourceViewController as? ViewController, 
        text = sourceViewController.cellText {
            // ^ note 2nd clause of if let statement above    
        if let selectedIndexPath = tableView.indexPathForSelectedRow {
            // Update cell text
            userTextLabels[selectedIndexPath.row] = text
            tableView.reloadRowsAtIndexPaths([selectedIndexPath], withRowAnimation: .None)
        }
    }
}

在这里,我们定义了一个展开动作,当它被触发时,它会检索视图控制器的cellText 属性,该属性是segue 的来源,即在我们的例子中,是ViewController 的实例。但是我们如何触发这个动作呢?

返回故事板和View Controller。注意View Controller 对象顶部的三个小图标,更具体地说,最右边的三个小图标,名为Exit。按住 Ctrl 键将动作从 Button 拖动到 Exit 图标,然后选择 unwindToTableView Action Segue。当您单击视图控制器按钮时,视图会展开(退出)并到达TableViewController 中的unwindToTableView 方法。

生成的应用应如下所示:


这比我预期的要长得多,但是一旦你开始写作......无论如何,上面的方法自然而然地不使用全局变量,而是使用对未来(prepareForSegue)或历史(@ 987654395@) 视图通过使用这些引用(对未来/历史视图)获取(通常从当前或历史视图)或设置(通常在当前或未来视图)值。

Apple 在 tableviewcontroller/viewcontroller 上下文中有一个关于示例应用程序的非常详尽的教程,我建议您复习一下。当我开始编写 Swift 代码时,我自己发现它非常有价值。

Start Developing iOS Apps (Swift)

【讨论】:

    猜你喜欢
    • 2015-06-16
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多