使用 Swift2 进行 iOS 开发 - 高级 iOS
在本章中,我们将介绍一些高级功能,例如在我们的应用程序上创建多个视图、添加导航栏、添加表格视图、在应用程序中存储数据、制作 Web 应用程序等。
请仔细阅读每个部分,因为本章包含了我们在开发应用程序时需要的大部分内容。
多视图控制器
在我们之前的应用程序中,我们只提供了一个视图/视图控制器。但是,我们的应用程序中可以有多个视图,我们可以独立执行其中任何一个。
因此,我们将从创建一个新项目开始;该项目的名称有多个视图。与其他所有项目一样,该项目也有一个视图控制器和一个用于该控制器的 Swift 文件。 (您可以通过选择视图并在 Identity Inspector 中查看其属性来查看。)
以下屏幕截图显示了我们当前视图的样子 −
在右侧(Identity Inspector),我们可以看到与我们的视图控制器相关的类。左侧的箭头是入口点。这是我们的应用程序开始运行后将显示的第一个视图。
添加第二个多视图控制器
要将其他视图控制器添加到我们的应用程序,我们将在对象库中搜索视图控制器。一旦找到它,我们将视图控制器拖到我们的 main.stroryboard,就在任何其他视图之外。
这就是您的应用程序应有的样子。现在,我们添加了一个视图控制器,但现在我们还需要为新添加的视图创建一个视图控制器类。
右键单击您的项目 → 新建文件 → cocoa Touch 类 → 将其命名为您想要的任何名称,我们将其命名为"SecondViewController"。
这就是您为视图控制器创建类文件的方法。现在,返回到"main.storyboard",单击第二个视图控制器并查看其 Identity Inspector。
类字段现在必须为空,因此单击该字段并开始输入您在上一步中添加的类名。如果出现,请单击 Enter。
我们现在已经创建了一个多视图控制器并为该视图添加了控制器类文件。但是,如果您运行该应用程序,它仍然不会显示您的第二个视图。为什么?
因为我们没有添加一个将带我们到该视图的函数。简而言之,我们还没有将导航添加到我们的应用程序中。别担心;我们将在下一节中介绍它。
向应用程序添加导航
从一个视图过渡到另一个视图的过程称为Segueing,即通过在两个视图之间创建 segue 来完成。为此,在第一个视图控制器中添加一个按钮,然后控制从它拖动到第二个视图。当您释放按钮时,您将看到一些选项,如下面的屏幕截图所示。
从 Action Segue 中选择 Show 选项。现在运行您的应用程序,您将看到单击按钮时,第二个视图出现(为了更清楚地看到,请在第二个视图中添加一些内容,以便您可以识别)。
但是,现在您无法返回到第一个视图。为此,我们有导航控制器。
添加导航控制器
选择您的第一个视图控制器,然后在顶部栏中单击编辑器 → 嵌入 → 导航控制器。
现在,我们的应用程序应如以下屏幕截图所示。
我们应该看到,视图顶部有一小行浅灰色。现在,当我们运行应用程序时,我们可以看到视图顶部有一个导航栏。当我们单击按钮时,我们将转到第二个视图,在那里我们将在该导航栏中看到一个后退按钮。单击此按钮,我们将返回到初始视图。
向导航栏添加标题和后退按钮
要向导航栏添加标题,请单击导航栏,然后查看其属性检查器。在那里我们将看到 −
Title − 这将是导航栏的标题,显示在中心。
Prompt − 显示在标题栏的顶部,位于中心。
Back Button − 您可以在此处修改后退按钮中显示的文本。
当前,传递视图的按钮位于我们的视图上,如果我们想在屏幕上显示其他内容,这可能不适合。因此,我们将在导航栏中添加一个栏按钮项,它将带我们进入第二个视图。但是,为此,我们应该首先删除我们添加的最后一个按钮。
添加栏按钮项
在对象库中搜索栏按钮项,并将其拖放到导航栏的右侧。将其命名为 – "Next >",将其拖动到第二个视图,选择"显示",就像我们添加的最后一个按钮一样。
现在运行应用程序,它看起来会更干净、更好。这就是我们现在对导航所做的一切。在后续章节中,我们将在需要时使用 Swift 代码修改导航栏。
表格视图
表格将数据呈现为包含多行的单列列表,可以进一步分为几部分。表格应该用来以干净、高效的方式呈现数据。
在本节中,我们将了解如何添加表格视图、添加原型单元格、为表格视图添加数据源和委托、更改表格的属性以及为表格视图单元格设置动态数据。
添加表格视图
要添加表格视图,我们首先创建一个新项目并将其命名为"tableView"。然后,转到对象库并搜索表格视图,我们将看到表格视图、表格视图控制器和许多其他选项。但是,我们应该选择表格视图,将其拖动并添加到默认视图控制器。
添加原型单元格
拉伸表格视图,使其覆盖整个视图,同时突出显示表格视图。检查它的属性检查器,有一个名为Prototype cells的字段,当前为0。我们应该将其值更改为1,现在你的视图应该如下所示 −
更改单元格标识符
现在,在您的视图中,单击您的原型单元格(这有点棘手)。因此,在您的文档大纲中,单击视图控制器→视图→表视图→表视图单元格,现在在其属性检查器中有一个名为标识符的列,单击它并将其命名为"单元格"。查看以下屏幕截图以了解上述步骤。
添加委托和数据源
为了使我们的表视图动态化,我们需要它们加载动态数据。因此,我们需要一个委托和一个数据源。要创建表的委托和数据源,请将控件从表视图拖到视图控制器或视图控制器顶部的黄色按钮,如下面的屏幕截图所示。
当我们释放光标时,我们将在那里看到两个选项,数据源和委托,逐个选择它们(当您选择任何一个选项时,弹出窗口将隐藏,您需要重复上述步骤来添加第二个选项)。现在它应该看起来像 −
这就是我们现在要对 UI / Main.Storyboard 进行的所有操作。现在切换到"ViewController.swift"文件。将 UITableViewDelegate、UITableViewDataSource 添加到您的 viewController.swift,如下所示 −
但是,现在 Xcode 会在此行中显示错误。
这是因为有几种方法我们需要使用 UITableView
要查看这些方法,请按住 Command + 单击 UITableViewDataSouce,然后复制前两个方法,其中包含"numberOfRowsInSection"、"cellForRowAtIndex"参数,并将它们粘贴到 ViewController.swift 中,在我们的viewDidLoad()。
从两个方法中删除此行 @available(iOS 2.0, *),并添加开始和结束括号"{}"。现在,视图将如下所示 −
Xcode 必须在两个函数中显示错误。但是,不要担心,因为这是因为我们没有添加这些函数的返回类型。
numberOfRowsInSection − 此函数定义我们的部分将包含的行数。所以现在将此行添加到您的方法中。
return 1 //这将仅返回一行。
cellForRowAt −此方法返回每个单元格的内容,indexPath包含每个单元格的索引。我们将创建一个单元格,然后为该单元格分配一些值,最后返回该单元格。
现在,您的函数应如下所示。 −
internal func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1; } internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "Cell") cell.textLabel?.text = "First Row" return cell }
在第一行中,我们创建了一个具有默认样式的单元格,reuseIdentifier 是我们制作的原型单元格的名称。
Cell.textLable?.text − 这定义了应作为该单元格标题出现的文本。
最后,我们从那里返回一个单元格。现在尝试运行您的应用程序,它应该如下所示 −
时间表应用程序
在此应用程序中,我们将继续我们的上一个项目,我们将制作一个应用程序,在其中打印 2(2... 10.... 20)的表格。
因此,要制作此应用程序,只需更改项目的视图控制器文件。
更改如下所示的功能 −
internal func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 10; } internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "Cell") }
现在,运行您的应用程序。它应该如下所示。
现在,我们已经完成了表格视图并制作了一个应用程序,所以这里有一个快速挑战需要我们解决。
挑战
制作一个应用程序,我们将打印用户输入的任何数字的计数表。
提示 − 接受输入,添加一个按钮,按下该按钮时将加载带有该数字计数的表格。这里我们还需要以下函数,它将重新加载表格数据。
tableView.reloadData()
这对您来说是一个挑战,因为我们已经涵盖了有关此应用程序的所有主题,因此我们不会为此提供解决方案。
鸡蛋计时器应用程序
在此应用程序中,我们将使用Timer()和类构造函数的概念来管理时间。我们将为您提供概念和编码。您必须自己制作 UI,因为我们已经在前面的章节中多次讨论过每个 UI 元素。 (尽管我们会为所有看起来很新的东西提供提示)。
您的最终应用布局应如下所示 −
此应用中会发生什么?
标题标签的起始值为 210。
单击播放按钮时,值应每秒减少一。
单击暂停时,值应仅停止在那里。
单击 -10 时,值应减少 10,并继续减少。
单击 +10 时,值应增加 10,并继续减少继续。
单击"重置"后,值应变为 210。
值绝不能低于 0。
概念
我们将使用 Timer() 类的变量 → var timer = Timer()。
我们将为刚刚创建的这个计时器变量设置一个值。
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.processTimer), userInfo: nil, repeats: true)
timeInterval -> 是我们想要使用的时间间隔,
target ->是应该受影响的视图控制器,
selector -> 是将使用此计时器的函数名称,
userInfo -> null 并重复,是的,我们想重复,所以它将是真的。
使计时器失效
要通过编程停止计时器,我们将添加 timer.invalidate() 函数。
我们使用的元素 −
导航栏 − 在导航栏中,我们添加了三个项目。
- 栏按钮项目,一个在左边,一个在右边。
- 标题命名为 – "我们的鸡蛋计时器"。
工具栏 −工具栏出现在应用屏幕的底部,包含用于执行与当前视图或其中内容相关的操作的按钮。
工具栏是半透明的,可能有背景色调。当人们不太可能需要它们时,它们通常会隐藏起来。
我们在 UI 底部添加了一个工具栏,其中包含 5 个项目。
- 三个栏按钮项目,分别名为 -10、Reset 和 +10。
- 两个灵活的空间:栏按钮项目之间的灵活空间 −
如何向栏按钮项目添加图标?
选择您的栏按钮项目。单击您的栏按钮项,转到属性检查器,单击"选择项",然后从出现的下拉列表中选择项。
同样,选择所有其他按钮的项并创建如上所示的 UI。在视图的中心添加一个标签并将其连接为出口,将其命名为 − timeLeftLabel。
启动计时器的操作
以下是启动计时器的程序。
@IBAction func startTimerButton(_ sender: Any) { if !timerActive { timerActive = true eggTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.processTimer), userInfo: nil, repeats: true) } }
创建以下函数 −
func stopTimer() { timerActive = false eggTimer.invalidate() }
停止函数的操作
以下是停止函数的程序。
@IBAction func pauseTimerButton(_ sender: Any) { stopTimer() }
减去时间的操作
以下是减去时间的程序。
@IBAction func subtractTime(_ sender: Any) { if timeLeft > 10 { timeLeft = timeLeft - 10 timeLeftLabel.text = String(timeLeft) } }
重置时间的操作
以下是重置时间的程序。
@IBAction func resetTimer(_ sender: Any) { timeLeft = 210 timeLeftLabel.text = String(timeLeft) }
addTime 的操作
以下是添加时间的程序。
@IBAction func addTime(_ sender: Any) { timeLeft = timeLeft + 10 timeLeftLabel.text = String(timeLeft) }
现在,viewController.swift 应该看起来像 −
import UIKit class ViewController: UIViewController { @IBOutlet weak var timeLeftLabel: UILabel! var eggTimer = Timer() // Initialize the Timer class. var timerActive = false // Prevents multiple timers from firing. var timeLeft = 210 func stopTimer() { timerActive = false eggTimer.invalidate() } func processTimer() { if timeLeft <= 0 { stopTimer() return } timeLeft = timeLeft - 1; timeLeftLabel.text = String(timeLeft) } @IBAction func startTimerButton(_ sender: Any) { if !timerActive { timerActive = true eggTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.processTimer), userInfo: nil, repeats: true) } } @IBAction func pauseTimerButton(_ sender: Any) { stopTimer() } @IBAction func subtractTime(_ sender: Any) { if timeLeft > 10 { timeLeft = timeLeft - 10 timeLeftLabel.text = String(timeLeft) } } @IBAction func resetTimer(_ sender: Any) { timeLeft = 210 timeLeftLabel.text = String(timeLeft) } @IBAction func addTime(_ sender: Any) { timeLeft = timeLeft + 10 timeLeftLabel.text = String(timeLeft) } override func viewDidLoad() { super.viewDidLoad() // 加载视图后执行任何其他设置,通常从 nib 进行。 } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // 处置所有可以重新创建的资源 } }
这就是我们在应用程序中要做的所有事情,尝试运行应用程序,它应该运行良好。
将数据存储在本地存储中
将数据存储在本地存储中意味着使用本地设备的存储来存储设备上与应用程序相关的数据。我们有两种方法将数据存储在本地存储中,即NSUserDefault和CoreData。
让我们详细了解它们。
NSUserDefaults
NSUserDefaults 用于存储小块数据,例如首选项、设置或单个值。要在我们的应用程序中使用 UserDefaults,我们只需通过我们的代码创建对 nsuserDefaults 的引用,如下所示。
let defaultValues = NSUserDefaults.standardUserDefaults()
要将值设置为 UserDefaults 中的数据,我们可以使用以下代码 −
defaultValues.setObject("Simplified iOS", forKey: "nameKey") func setDouble(value: Double, forKey defaultName: String) func setBool(value: Bool, forKey defaultName: String) func setObject(value: AnyObject?, forKey defaultName: String) func setURL(url: NSURL?, forKey defaultName: String) func setInteger(value: Int, forKey defaultName: String) func setFloat(value: Float, forKey defaultName: String)
要从 NSUserDefaults 获取值,我们可以使用以下代码。
func boolForKey(defaultName: String) -> Bool func integerForKey(defaultName: String) -> Int func floatForKey(defaultName: String) -> Float func doubleForKey(defaultName: String) -> Double func objectForKey(defaultName: String) -> AnyObject? func URLForKey(defaultName: String) -> NSURL?
CoreData
CoreData 是一个持久性框架,支持大数据事务。CoreData 允许您构建关系实体属性模型来存储用户数据。CoreData 是一个框架,可以使用 SQLite 二进制格式来存储数据。
要在我们的应用程序中使用 CoreData,我们将从一个新项目开始,并确保在创建项目时选中"使用 Core Data"。
使用核心数据登录 −创建一个新项目,选择使用 CoreData,如以下屏幕截图所示。
继续,直到项目打开,现在我们看到该项目比我们以前的项目拥有更多文件。
此文件 CoreData_demo.xcdatamodeld 是我们的数据库,我们将在其中创建用户表并存储数据。
概念 −关于 CoreData 的事情是,即使我们关闭应用程序,几个月后再打开它,它仍然会保留我们存储的数据,我们将在下一个应用程序中看到这些数据。
现在我们将了解如何添加核心数据和检索核心数据。
添加核心数据 − 要添加 CoreData,请单击文件 CoreData_demo.xcdatamodeld,然后我们将看到实体为空。单击添加实体按钮,它将添加一个实体,现在双击实体名称并将其重命名为您喜欢的任何名称。
现在单击实体,我们可以看到属性字段为空。单击加号并重命名实体。从下一个字段中选择实体的类型。
我们在其中添加了一个实体和一个属性。现在,如果我们转到 AppDelegate.swift,我们可以看到由于选择了 CoreData,因此添加了两个新函数。添加的两个函数是 −
注意 − 在继续操作之前,请先在文件中导入 CoreData。
将数据保存到 Core Data − 要在 CoreData 中保存一些数据,我们需要创建一个 AppDelegate 类的对象。
let appDelegate = UIApplication.shared.delegate as! AppDelegate
还有一个上下文对象
let context = appDelegate.persistentContainer.viewContext
然后,我们需要创建一个实体对象,它将调用我们的实体 −
let newValue = NSEntityDescription.insertNewObject(forEntityName: "Users", into: context)
我们现在将设置我们创建的该属性的值。
newValue.setValue(textField.text, forKey: "name")
我们将使用 save 保存数据
context.save();
从核心数据中获取 − 获取时,上述两个步骤(创建 appDelegate 和 context)相同。然后,我们将创建一个获取请求。
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users")
我们将创建一个对象来存储结果。
let results = try context.fetch(request)
然后我们将根据我们的要求查看结果。我们将在创建的下一个应用程序中看到更多 CoreData。
挑战 − 尝试创建一个应用程序,用户输入名称,然后单击登录并关闭应用程序。当用户下次打开应用程序时,他应该仍处于登录状态。然后添加一个按钮 - 注销,如果他点击该按钮,应用程序将再次要求输入用户名。
使用 CoreData 登录/注销
创建一个名为"登录"的单视图项目,选择使用 CoreData。单击 CoreData_demo.xcdatamodeld 并添加一个名为"用户"的实体。在其中,添加一个名为"名称"的属性。
转到 main.storyboard,添加一个文本字段和一个登录按钮。在其下方,添加一个标签,双击它并删除其内容。然后,添加一个注销按钮,转到其属性检查器并将"alpha"设置为 0。现在,我们的视图应如下所示 −
现在,转到您的视图控制器文件,打开助理编辑器并在 UI 元素和控制器文件之间创建连接。
注意 − 我们还将为这两个按钮创建出口,因为我们需要修改这些按钮的外观。例如 − 当用户登录时,我们将隐藏登录按钮,如果用户未登录,我们将显示登录并隐藏注销按钮。
正如我们已经讨论过的从 CoreData 添加和获取数据,我们将在此处放上代码。
Try-Catch −您会注意到我们在代码中多次使用了 try-catch 块。这是因为如果我们不使用 try-catch 块并且程序中出现某些异常或错误,则执行将停止。然而,如果我们使用 try catch 块并且如果发生某些错误,则 catch 块会处理该错误。在我们的 Swift 教程
中阅读更多相关信息登录/注销应用程序的代码
让我们了解用于登录/注销应用程序的不同组件和代码。
登录按钮操作 − 以下代码说明如何添加登录按钮操作。
var isLoggedIn = false @IBAction func logIn(_ sender: AnyObject) { let appDelegate = UIApplication.shared.delegate as! AppDelegate let context = appDelegate.persistentContainer.viewContext if isLoggedIn { let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users") do { let results = try context.fetch(request) if results.count > 0 { for result in results as! [NSManagedObject] { result.setValue(textField.text, forKey: "name") do { try context.save() } catch { print("Update username failed") } } label.text = "Hi " + textField.text! + "!" } } catch { print("Update failed") } } else { let newValue = NSEntityDescription.insertNewObject(forEntityName: "Users", into: context) newValue.setValue(textField.text, forKey: "name") do { try context.save() logInButton.setTitle("Update username", for: []) label.alpha = 1 label.text = "Hi " + textField.text! + "!" isLoggedIn = true logOutButton.alpha = 1 } catch { print("Failed to save") } } }
注销按钮操作 − 以下代码说明如何添加注销按钮操作。
@IBAction func logOut(_ sender: AnyObject) { let appDelegate = UIApplication.shared.delegate as! AppDelegate let context = appDelegate.persistentContainer.viewContext let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users") do { let results = try context.fetch(request) if results.count > 0 { for result in results as! [NSManagedObject] { context.delete(result) do { try context.save() } catch { print("Individual delete failed") } } label.alpha = 0 logOutButton.alpha = 0 logInButton.setTitle("Login", for: []) isLoggedIn = false textField.alpha = 1 } } catch { print("Delete failed") } }
ViewDidLoad() − 以下代码解释了如何使用 ViewDidLoad() 函数。
override func viewDidLoad() { super.viewDidLoad() // 加载视图后执行任何其他设置,通常从 nib 进行。 let appDelegate = UIApplication.shared.delegate as! AppDelegate let context = appDelegate.persistentContainer.viewContext let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users") request.returnsObjectsAsFaults = false do { let results = try context.fetch(request) for result in results as! [NSManagedObject] { if let username = result.value(forKey: "name") as? String { logInButton.setTitle("Update username", for: []) logOutButton.alpha = 1 label.alpha = 1 label.text = "Hi there " + username + "!" } } } catch { print("Request failed") } }
请记住,您必须为两个按钮创建一个出口和一个操作。
现在,保存并运行应用程序。登录,关闭应用程序并再次运行。它应该如下所示。
这就是我们将使用 CoreData 进行的所有操作。使用相同的概念,我们可以构建许多 CoreData 应用程序。
控制键盘
在本节中,我们将学习如何控制键盘行为。例如 - 当我们在输入一些文本后单击文本字段外部时,键盘不会关闭。在这里,我们将了解如何控制键盘。
单击输入字段外部时键盘应消失
这是一项简单的任务,要做到这一点,只需将以下代码粘贴到您的 viewController 文件中,在关闭花括号之前。
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { self.view.endEditing(true) }
通过这样做,它将使键盘在单击输入字段外部时消失。
点击返回键时键盘应消失
要使键盘消失,我们应该为我们的视图控制器添加一个新的类型。我们还将添加一个文本字段并创建名为 textField 的出口。最后,我们将添加 UITextFieldDelegate。
我们还将 控制 + 拖动 从我们的输入字段到视图控制器,并从出现的选项中选择委托。
然后,我们将添加以下函数。
func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true }
最终的视图控制器文件应如下所示 −
import UIKit class ViewController: UIViewController, UITextFieldDelegate { override func viewDidLoad() { super.viewDidLoad() // 加载视图后执行任何其他设置,通常从 nib 进行。 } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // 处理所有可以重新创建的资源。 } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { self.view.endEditing(true) } func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true } }
下载 Web 内容 − 打开 Facebook/Google
在本节中,我们将学习如何制作一个应用程序,当按下每个按钮时,该应用程序将打开 Facebook 和 Google。我们还将学习 Web 视图和应用传输层安全性的概念。在此之后,您将能够制作自己的浏览器。
注意 − 我们需要此应用程序中的 Internet 连接。
制作 Web 应用程序
我们将制作一个新的单视图应用程序,iOS 项目。在对象库的搜索栏中,我们将搜索 Web 视图,将其拖动并将其添加到 main.Storyboard 中的视图控制器中。
添加 Web 视图后,我们将把它拉伸到所有角落。应用程序 UI 应如下所示 −
我们将通过单击助手编辑器打开 main.storyboard 和视图控制器。我们将为 webView 创建一个出口,并为两个按钮创建一个操作。加载时,应用程序将在 webView 中加载 yahoo。单击 google 时,它应该加载 Google,单击 Facebook 按钮时,它应该加载 Facebook 页面。
最终视图应如下所示 −
以下屏幕截图显示了我们的应用程序的不同屏幕应是什么样子。如果您尝试打开非 https 的 Web 服务,则会显示错误,我们必须在您的 info.plist 文件中添加应用传输层安全性例外。