Swift 中的全局变量和可选绑定
Global variable and optional binding in Swift
我对可选绑定、全局变量和包装和展开有一些非常简单的疑问。由于我是 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) 让我通过下面的例子让自己更清楚——我的项目中有 2 VC VC1 & VC2 。 VC2 有一个文本字段,用户可以在其中输入一些值并将其显示在 VC1 的表格视图中。
在 Vc1
var namevc1 = NSMutableArray?//holds the input of textfield to be passed from VC2.
如您所见,我的 VC1 是第一个在我的项目运行时加载的视图控制器,我正在使用一个可选变量来填充我的 tabke vuew,即
'arr'
所以当应用程序第一次运行时它是空的。因此在代码中使用它的值时可能会导致致命错误。那么在
中是否解绑它的解决方法是什么
viewDidLoad()
方法或全部声明一个空的 NSMutable 数组类型来代替可选类型。
提前致谢。
我将从重复上面的评论开始。
可能你误解了Swift中全局变量的概念。
如果你有一个全局变量,你不必在任何 views/methods/classes 等之间 "pass" 它,因为该变量是在全局范围内定义的(可在任何地方访问) .
一般来说,全局变量不是一个好主意,也是你要避免的事情。
关于全局变量和swift的事情,确实应该把singletons也包括进来讨论。参见例如以下现有 SO 线程:
(How to create a global variable?)
(Declaring Global Variables in Swift)
表ViewController和ViewController之间通过segues的通信(准备和展开segues)
(这个答案最终非常彻底,可能有点太彻底了,因为我没有详细了解您当前的 tableview/viewcontroller 程序状态是什么样的。很抱歉回答冗长以及可能给读者带来的任何不便).
现在,让我们离开全局变量并讨论一个(除其他外)可行的选项,用于在您的示例中的两个控制器之间进行通信。根据您的问题,我将您的示例总结如下
- VC1:故事板入口点,由
UITableViewCell
组成的UITableViewController
,其中,在这些单元格,你显示一些文本,比如说,通过 UILabel
. 的实例
- VC2:一个
UIViewController
,可从 VC1 的单元访问,包含一个 UITextField
实例。当用户在此文本字段中输入文本时,您希望文本显示在 VC2 中关联的 单元格 中(关联的意思是它是 VC1 中用于访问 VC2 的单元格).
我们将 VC1 和 VC2 与 (cocoa touch) classes TableViewController
(TableViewController.swift) 和ViewController
(ViewController.swift),分别。 table view controller 中的 cells 将关联 (cocoa touch) class TableViewCell
(TableViewCell.swift) .这些 class 的详细信息如下。
对于这个简单的示例,请注意我们不会将 VC1 嵌入到导航控制器中(否则适用于 table 视图 -> 视图导航)。
我们将从故事板开始,为 Table View Controller
和 View Controller
添加对象(从对象库中拖放)。 table 视图容器还将自动在其 Table View
中包含一个 TableViewCell
。在情节提要中继续:
- 在
Table View Controller
中的TableViewCell
容器中添加一个UILabel
对象(随意对齐)
- 在
View Controller
中添加一个Text Field
对象和一个Button
对象(随意对齐)。
- 将入口点设置为
Table View Controller
。
- 然后按住 Ctrl 键将 'Show' segue 从
TableViewCell
拖到 View Controller
。
- Select
Show
segue,然后从属性检查器中为其输入标识符,例如 ShowDetail.
- 最后,使用
TableViewCell
selected,(如上;来自属性检查器),输入单元格的标识符。在这里,我们将简单地使用标识符 TableViewCell.
我们现在暂时离开故事板并实现三个 classes,与 Table View Controller
、View Controller
和前者的 TableViewCell
.[=84 相关联=]
我们从 Table View Controller
开始,实现我们的 UITableViewController
子 class。请注意,在这里,我们不使用 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
中使用的标准方法,用于 [=242] 中的节数(例如 return 1
)和单元格(例如 return (numberOfCells ?? 0)
) =],分别。我会把这些留给你。
现在,我们将 table 视图中的 TableViewCell
对象与 class 到 UITableViewCell
的子实例相关联。在这里,我们将为我们的单元格使用一个非常简单的 class;每个单元格仅包含一个 UILabel
实例(通过故事板 Ctrl-拖动作为 table 视图单元格中 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)
}
}
最后,对于从 table 视图单元访问的视图控制器:使用单个 @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之间通信的容器。
我们 return 到情节提要并---从身份检查器---将三个情节提要对象(Table View Controller
、View Controller
、TableViewCell
)与它们的相关 class 就是我们刚刚在上面写的。
我们现在接近目标了;它只剩下指定如何在两个控制器之间进行通信。
我们将从从 VC1 到 VC2 的通信开始。在您上面的评论中,通过查看 prepareForSegue(...)
方法,您走在了正确的轨道上(无论如何对于这个特定的解决方案)。在class中为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
的任何现有文本带入发件人单元格(由字符串数组 [=64 指定) =]).查看 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)
}
}
}
在这里,我们定义了一个展开操作,当它被触发时,检索作为 segue 源的视图控制器的 cellText
属性,即在我们的例子中,实例ViewController
。但是我们如何触发这个动作呢?
Return 到故事板和 View Controller
。请注意 View Controller
对象顶部的三个小图标,更具体地说,是最右侧的三个小图标,名为 Exit
。按住 Ctrl 键将动作从 Button
拖到 Exit
图标,然后 select 拖到 unwindToTableView
动作 Segue。当您单击视图控制器按钮时,视图展开(退出)并降落在 TableViewController
.
中的 unwindToTableView
方法处
生成的应用程序应如下所示:
这比我预期的要长很多,但是一旦你开始写...无论如何,上面的方法自然没有使用全局变量,而是使用对 future 的引用 (prepareForSegue
)或历史 (unwindToTableView
) 视图通过使用这些引用(对 future/historic 视图)获取(通常从当前或历史视图)或设置(通常在当前或未来视图中)值。
Apple 在 tableviewcontroller/viewcontroller 上下文中有自己非常详尽的示例应用程序教程,我建议您仔细阅读。当我开始编码时,我自己发现它非常有价值 Swift.
我对可选绑定、全局变量和包装和展开有一些非常简单的疑问。由于我是 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) 让我通过下面的例子让自己更清楚——我的项目中有 2 VC VC1 & VC2 。 VC2 有一个文本字段,用户可以在其中输入一些值并将其显示在 VC1 的表格视图中。
在 Vc1
var namevc1 = NSMutableArray?//holds the input of textfield to be passed from VC2.
如您所见,我的 VC1 是第一个在我的项目运行时加载的视图控制器,我正在使用一个可选变量来填充我的 tabke vuew,即
'arr'
所以当应用程序第一次运行时它是空的。因此在代码中使用它的值时可能会导致致命错误。那么在
中是否解绑它的解决方法是什么viewDidLoad()
方法或全部声明一个空的 NSMutable 数组类型来代替可选类型。
提前致谢。
我将从重复上面的评论开始。
可能你误解了Swift中全局变量的概念。
如果你有一个全局变量,你不必在任何 views/methods/classes 等之间 "pass" 它,因为该变量是在全局范围内定义的(可在任何地方访问) .
一般来说,全局变量不是一个好主意,也是你要避免的事情。
关于全局变量和swift的事情,确实应该把singletons也包括进来讨论。参见例如以下现有 SO 线程:
(How to create a global variable?)
(Declaring Global Variables in Swift)
表ViewController和ViewController之间通过segues的通信(准备和展开segues)
(这个答案最终非常彻底,可能有点太彻底了,因为我没有详细了解您当前的 tableview/viewcontroller 程序状态是什么样的。很抱歉回答冗长以及可能给读者带来的任何不便).
现在,让我们离开全局变量并讨论一个(除其他外)可行的选项,用于在您的示例中的两个控制器之间进行通信。根据您的问题,我将您的示例总结如下
- VC1:故事板入口点,由
UITableViewCell
组成的UITableViewController
,其中,在这些单元格,你显示一些文本,比如说,通过UILabel
. 的实例
- VC2:一个
UIViewController
,可从 VC1 的单元访问,包含一个UITextField
实例。当用户在此文本字段中输入文本时,您希望文本显示在 VC2 中关联的 单元格 中(关联的意思是它是 VC1 中用于访问 VC2 的单元格).
我们将 VC1 和 VC2 与 (cocoa touch) classes TableViewController
(TableViewController.swift) 和ViewController
(ViewController.swift),分别。 table view controller 中的 cells 将关联 (cocoa touch) class TableViewCell
(TableViewCell.swift) .这些 class 的详细信息如下。
对于这个简单的示例,请注意我们不会将 VC1 嵌入到导航控制器中(否则适用于 table 视图 -> 视图导航)。
我们将从故事板开始,为 Table View Controller
和 View Controller
添加对象(从对象库中拖放)。 table 视图容器还将自动在其 Table View
中包含一个 TableViewCell
。在情节提要中继续:
- 在
Table View Controller
中的TableViewCell
容器中添加一个UILabel
对象(随意对齐) - 在
View Controller
中添加一个Text Field
对象和一个Button
对象(随意对齐)。 - 将入口点设置为
Table View Controller
。 - 然后按住 Ctrl 键将 'Show' segue 从
TableViewCell
拖到View Controller
。 - Select
Show
segue,然后从属性检查器中为其输入标识符,例如 ShowDetail. - 最后,使用
TableViewCell
selected,(如上;来自属性检查器),输入单元格的标识符。在这里,我们将简单地使用标识符 TableViewCell.
我们现在暂时离开故事板并实现三个 classes,与 Table View Controller
、View Controller
和前者的 TableViewCell
.[=84 相关联=]
我们从 Table View Controller
开始,实现我们的 UITableViewController
子 class。请注意,在这里,我们不使用 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
中使用的标准方法,用于 [=242] 中的节数(例如 return 1
)和单元格(例如 return (numberOfCells ?? 0)
) =],分别。我会把这些留给你。
现在,我们将 table 视图中的 TableViewCell
对象与 class 到 UITableViewCell
的子实例相关联。在这里,我们将为我们的单元格使用一个非常简单的 class;每个单元格仅包含一个 UILabel
实例(通过故事板 Ctrl-拖动作为 table 视图单元格中 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)
}
}
最后,对于从 table 视图单元访问的视图控制器:使用单个 @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之间通信的容器。
我们 return 到情节提要并---从身份检查器---将三个情节提要对象(Table View Controller
、View Controller
、TableViewCell
)与它们的相关 class 就是我们刚刚在上面写的。
我们现在接近目标了;它只剩下指定如何在两个控制器之间进行通信。
我们将从从 VC1 到 VC2 的通信开始。在您上面的评论中,通过查看 prepareForSegue(...)
方法,您走在了正确的轨道上(无论如何对于这个特定的解决方案)。在class中为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
的任何现有文本带入发件人单元格(由字符串数组 [=64 指定) =]).查看 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)
}
}
}
在这里,我们定义了一个展开操作,当它被触发时,检索作为 segue 源的视图控制器的 cellText
属性,即在我们的例子中,实例ViewController
。但是我们如何触发这个动作呢?
Return 到故事板和 View Controller
。请注意 View Controller
对象顶部的三个小图标,更具体地说,是最右侧的三个小图标,名为 Exit
。按住 Ctrl 键将动作从 Button
拖到 Exit
图标,然后 select 拖到 unwindToTableView
动作 Segue。当您单击视图控制器按钮时,视图展开(退出)并降落在 TableViewController
.
unwindToTableView
方法处
生成的应用程序应如下所示:
这比我预期的要长很多,但是一旦你开始写...无论如何,上面的方法自然没有使用全局变量,而是使用对 future 的引用 (prepareForSegue
)或历史 (unwindToTableView
) 视图通过使用这些引用(对 future/historic 视图)获取(通常从当前或历史视图)或设置(通常在当前或未来视图中)值。
Apple 在 tableviewcontroller/viewcontroller 上下文中有自己非常详尽的示例应用程序教程,我建议您仔细阅读。当我开始编码时,我自己发现它非常有价值 Swift.