使用 Swift2 进行 iOS 开发 - 高级 iOS
在本章中,我们将介绍一些高级功能,例如在应用程序上创建多个视图、添加导航栏、添加表视图、在应用程序中存储数据、制作 Web 应用程序等。
请仔细阅读每一节,因为本章包含了我们开发应用程序时需要的大部分内容。
多视图控制器
在我们之前的应用程序中,我们只给出了单个视图/视图控制器。但是,我们的应用程序中可以有多个视图,并且可以独立地对其中任何一个执行操作。
因此,我们将从创建一个新项目开始;这个项目的名字有多种说法。与其他所有项目一样,该项目也有一个视图控制器和该控制器的 Swift 文件。(您可以通过选择视图并在身份检查器中查看其属性来看到这一点。)
以下屏幕截图显示了我们当前视图的样子 -
在右侧(身份检查器),我们可以看到与视图控制器相关的类。左侧的这个箭头是入口点。这是应用程序开始运行后显示的第一个视图。
添加第二个多视图控制器
要将其他视图控制器添加到我们的应用程序中,我们将在对象库中搜索视图控制器。一旦找到它,我们将把视图控制器拖到我们的 main.storyboard 上,就在任何其他视图之外。
这就是您的应用程序应该的样子。现在,我们已经添加了一个视图控制器,但现在我们还需要为新添加的视图创建一个视图控制器类。
右键单击您的项目→新建文件→cocoa Touch Class→将其命名为您想要的任何名称,我们将其命名为“SecondViewController”。
这就是为视图控制器创建类文件的方法。现在,返回“main.storyboard”,单击第二个视图控制器并查看其身份检查器。
班级字段现在必须为空,因此单击该字段并开始输入您在上一步中添加的班级名称。如果出现,请单击输入。
我们现在已经创建了一个多视图控制器并为该视图添加了控制器类文件。但是,如果您运行该应用程序,它仍然不会显示您的第二个视图。为什么?
因为我们还没有添加一个函数,它将把我们带到那个视图。简而言之,我们尚未将导航添加到我们的应用程序中。不用担心; 我们将在下一节中介绍它。
向应用程序添加导航
从一个视图转换到另一个视图的过程称为Segueing,即通过在两个视图之间创建 Segue 来完成。为此,请在第一个视图控制器中添加一个按钮,然后控制从该按钮拖动到第二个视图。当您释放按钮时,您将看到一些选项,如下面的屏幕截图所示。
从“操作片段”中选择“显示”选项。现在运行您的应用程序,您将看到单击按钮后,会出现第二个视图(为了更清楚地在第二个视图中添加一些内容,以便您可以识别)。
但是,现在您无法返回到第一个视图。为此,我们有导航控制器。
添加导航控制器
选择您的第一个视图控制器,然后在顶部栏中单击编辑器 → 嵌入 → 导航控制器。
现在,我们的应用程序应如下图所示。
我们应该注意,视图顶部有一个浅灰色的小行。现在,当我们运行应用程序时,我们可以看到视图顶部有一个导航栏。当我们单击该按钮时,我们将进入第二个视图,在该视图中我们将在导航栏中看到后退按钮。单击此按钮,我们将返回到初始视图。
将标题和后退按钮添加到导航栏
要向导航栏添加标题,请单击导航栏,然后查看其属性检查器。在那里我们会看到 -
标题- 这将是导航栏的标题,出现在中心。
提示- 它出现在标题栏顶部的中央。
返回按钮- 在这里您可以修改显示在返回按钮中的文本。
目前,传递视图的按钮位于我们的视图上,如果我们希望屏幕上显示其他内容,这可能不适合。因此,我们将在导航栏中添加一个 Bar Button 项,这将带我们进入第二个视图。然而,为此我们应该首先删除我们添加的最后一个按钮。
添加栏按钮项目
在对象库中搜索栏按钮项目并将其拖放到导航栏的右侧。将其命名为 - “Next >”,将控件从其拖动到第二个视图,选择“显示”,就像我们对添加的最后一个按钮所做的那样。
现在运行该应用程序,它看起来会更干净、更好。这就是我们现在对导航要做的全部事情。在后续章节中,我们将根据需要使用 Swift 代码修改导航栏。
表格视图
表将数据显示为包含多行的单列列表,这些行可以进一步分为多个部分。表格应该用于以干净、有效的方式呈现数据。
在本节中,我们将了解如何添加表视图、添加原型单元格、为表视图添加数据源和委托、更改表的属性以及为表视图单元格设置动态数据。
添加表视图
要添加表格视图,我们首先创建一个新项目并将其命名为“tableView”。然后,进入对象库并搜索Table View,我们将看到表视图、表视图控制器和许多其他选项。但是,我们应该选择表视图,将其拖动并添加到默认视图控制器中。
添加原型单元
拉伸表视图,使其覆盖整个视图,同时表视图会突出显示。检查它的属性检查器,有一个名为 Prototype cells 的字段,当前为 0。我们应该将其值更改为 1,现在您的视图应如下所示 -
更改单元格标识符
现在,在您的视图中,单击原型单元(这有点棘手)。因此,在文档大纲中,单击“视图控制器”→“视图”→“表格视图”→“表格视图单元格”,现在在其属性检查器中有一列名为“标识符”的列,单击该列并将其命名为“单元格”。请参阅以下屏幕截图以了解上述步骤。
添加委托和数据源
为了使我们的表视图动态化,我们需要它们加载动态数据。因此,我们需要一个委托和一个数据源。要创建表的委托和数据源,请控制从表视图拖动到视图控制器或视图控制器顶部的黄色按钮,如下面的屏幕截图所示。
当我们松开光标时,我们会看到两个选项,dataSource和delegate,一一选择它们(当您选择任何一个选项时,弹出窗口将隐藏,您需要重复上述步骤来添加第二个选项) 。现在它应该看起来像 -
这就是我们现在要对 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 //This will return only one row.
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.scheduledTimer(timeInterval: 1, 目标: self, 选择器: #selector(ViewController.processTimer), userInfo: nil, 重复次数: true)
timeInterval -> 是我们要使用的时间间隔,
target -> 是应该生效的视图控制器,
选择器 -> 是将使用此计时器的函数名称,
userInfo -> null 并重复,是的,我们想重复,所以它会是真的。
使定时器失效
要通过编程停止计时器,我们将添加timer.invalidate()函数。
我们使用的元素-
导航栏- 在导航栏中,我们添加了三个项目。
- 栏按钮项目,左侧一个,右侧一个。
- 标题名为“我们的煮蛋计时器”。
工具栏- 工具栏出现在应用程序屏幕的底部,包含用于执行与当前视图或其中内容相关的操作的按钮。
工具栏是半透明的,并且可能有背景色调。当人们不太可能需要它们时,它们经常隐藏起来。
我们在 UI 底部添加了一个工具栏,其中有 5 个项目。
- 三个条形按钮项目,分别命名为 -10、重置和 +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() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated } }
这就是我们在应用程序中要做的所有事情,尝试运行应用程序,它应该运行良好。
将数据存储在本地存储上
将数据存储在本地存储上是指使用本地设备的存储来存储与设备上的应用程序相关的数据。我们有两种方法将数据存储在本地存储上,即NSUserDefault和CoreData。
让我们详细了解它们。
NS用户默认值
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是一个框架,可以使用SQLite、二进制格式来存储数据。
要在我们的应用程序中使用 CoreData,我们将从一个新项目开始,并确保在创建项目时选中“使用 Core Data”。
使用 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")
我们将使用保存数据
context.save();
从核心数据中获取- 获取时,上述两个步骤(创建 appDelegate 和上下文)将是相同的。然后,我们将创建一个获取请求。
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users")
我们将创建一个对象来存储结果。
let results = try context.fetch(request)
然后我们将根据我们的要求检查结果。我们将在创建的下一个应用程序中看到更多 CoreData 的内容。
挑战- 尝试创建一个应用程序,用户在其中输入名称,然后单击登录并关闭应用程序。当用户下次打开应用程序时,他应该仍然处于登录状态。然后添加一个按钮 - 注销,如果他单击该按钮,应用程序将再次要求输入用户名。
使用 CoreData 登录/注销
创建一个名为“Login”的单一视图项目,选择使用 CoreData。单击 CoreData_demo.xcdatamodeld 并添加一个名为“Users”的实体。在其中添加一个名为“name”的属性。
转到 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() // Do any additional setup after loading the view, typically from a 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 应用程序。
控制键盘
在本节中,我们将学习控制键盘Behave。例如 – 当我们在输入一些文本后单击文本字段外部时,键盘不会关闭。在这里,我们将了解如何控制键盘。
单击输入字段外部时键盘应该消失
这是一个简单的任务,只需将以下代码粘贴到您的 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() // Do any additional setup after loading the view, typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { self.view.endEditing(true) } func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true } }
下载网页内容 - 打开 Facebook/Google
在本节中,我们将学习如何制作一个应用程序,当分别按下每个按钮时,该应用程序将打开 Facebook 和 Google。我们还将学习 Web 视图和应用程序传输层安全性的概念。之后,您将能够制作自己的浏览器。
注意- 我们在此应用程序中需要互联网连接。
制作网络应用程序
我们将制作一个新的单视图应用程序,iOS 项目。在对象库的搜索栏中,我们将搜索 Web 视图,将其拖动并将其添加到 main.Storyboard 中的视图控制器中。
添加网络视图后,我们将其拉伸到所有角落。应用程序用户界面应如下所示 -
我们将通过单击助理编辑器打开 main.storyboard 和视图控制器。我们将为我们的 webView 创建一个出口,并为两个按钮创建一个操作。加载时,应用程序将在 webView 中加载 yahoo。单击 google 时,应加载 Google,单击 Facebook 按钮时,应加载 Facebook 页面。
最终视图应如下所示 -
以下屏幕截图显示了我们应用程序的不同屏幕的外观。如果您尝试打开非 https 的 Web 服务,它将显示错误,我们必须在您的info.plist文件中添加应用程序传输层安全例外。