iOS Apps 开发(Swift)(9)——Implement Navigation
来源:互联网 发布:窄斑凤尾蛱蝶 标本淘宝 编辑:程序博客网 时间:2024/06/05 14:53
前言:网上一直没有找到用Swift开发IOS的好的教程,所以找了官网的文档翻译一下算了。如有错误欢迎指正。博主首发CSDN,mcf171专栏。
博客链接:mcf171的博客
原文链接:Implement Navigation
——————————————————————————————
在本次课程中,我们将使用导航控制器并且创建FoodTracker app的导航控制流,在课程的最后我们将有一个完整的当行模式和交互流。当我们最终完成app之后,界面如图所示
学习目标
在本次课程中,你可以了解到:
- 在 storyboard 的导航控制器中嵌入一个已有的 view controller
- 在 view controller 之间创建segues
- 通过属性查看器编辑在storyboard 中 segue 的属性
- 使用prepareForSegue方法在 view controllers 之前传输数据
- 使用一个解绑的 segue
- 使用stack view 来创建鲁邦、灵活性强的布局
给向前导航增加一个Segue
当数据如我们期望的那样进行显示的时候,我们就可以给我们的菜品场景添加一个导航。场景之前的切换也就称为Segues
配置导航栏
现在我们需要给菜单列表添加一个标题,和一个导航切换的按钮。导航栏的标题由当前显示的试图控制器决定。因此我们通过使用菜品列表中的项来修改导航栏的标题,而不是直接写死了。
配置菜品列表的导航栏
使用自动布局完成最终的UI
从我们最初的UI设计到现在我们已经学了很多知识了。尽管我们对于UI做了很多的改动,但是到目前为止,我们不需要做任何更多的布局变动,不过我们需要确保布局中一切元素都是自动布局的。
为了达到这个效果我们需要一些简单的调整
使用 stack view 来更新布局
在菜品列表中存储一个新的菜品
下一步我们需要实现的功能就是添加一个新菜品。具体来说就是当一个用户在菜品场景输入一个菜品名、打分和照片并点击Save 按钮的时候,我们希望MealViewController 来创建Meal 对象,并且将这些信息传给MealTableViewController从而在菜品列表中进行展示。
首先我们在MealViewController中添加一个 Meal属性
在MealViewController中添加一个Meal 属性
1、打开MealViewController.swift
2、在ratingControl outlet 下 添加下述代码
/*This value is either passed by `MealTableViewController` in `prepareForSegue(_:sender:)`or constructed as part of adding a new meal.*/var meal: Meal?上述代码声明了一个可选类型的 Meal,也就是可能为nil
我们现在关心的是如何设置这个Meal 并且当点击Save 按钮的时候传输这个对象。为了知道 Save 按钮什么时候被点击了,我们需要在MealViewController.swift中添加一个 outlet
连接 Save 按钮和MealViewController
1、打开Storyboard
2、打开助手编辑器
3、如果需要更多的工作空间,可以折叠左右两个工作区
4、选择Save 按钮
5、使用Control + 拖拽将Save 按钮从画布中拖到代码中的ratingControl属性下。
6、输入saveButton
7、点击Connect
创建一个松弛切换(Unwind Segue)
现在我们的任务是当用户点击保存按钮的时候将Meal对象传输到MealTableViewController,并且当用户点击取消按钮的时候不管这个对象,同时将场景从菜品场景切换到菜品列表场景
为了实现上述功能,我们需要使用一个松弛的切换。一个松弛切换可以寄通过一个一个或多个切换来向后操作,同时返回用户已有视图控制器的实例。我们使用松弛切换来实现反转导航。
当一个切换被触发的时候,我们可以在触发代码中添加我们想执行的代码。这个方法是prepareForSeque,并且在这个方法中,我们也可以存储数据并且在切换的来源视图中做一些必要的清理工作。
在MealViewController中添加prepareForSeque方法
1、返回标准编辑器
2、打开MealViewController.swift
3、在//MARK:Actions 节上添加以下代码
// MARK: Navigation4、在注视下添加以下代码
// This method lets you configure a view controller before it's presented.override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {}
5、在prepareForSegue方法中添加if语句
if saveButton === sender {}我们使用了身份确定符号(===)来验证这个对象是不是saveButton outlet 对象
6、在 if 语句中添加以下语句
let name = nameTextField.text ?? ""let photo = photoImageView.imagelet rating = ratingControl.rating
上述代码创建了三个常量。注意两个问号,这个是空合并操作符(nil coalescing operator),目的就是如果name有值则返回值,没有则返回默认值。
7、在 if 语句中添加以下代码
// Set the meal to be passed to MealTableViewController after the unwind segue.meal = Meal(name: name, photo: photo, rating: rating)
上述代码设置了 meal 属性的值
目前 prepareForSegue 方法为:
// This method lets you configure a view controller before it's presented.override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if saveButton === sender { let name = nameTextField.text ?? "" let photo = photoImageView.image let rating = ratingControl.rating // Set the meal to be passed to MealTableViewController after the unwind segue. meal = Meal(name: name, photo: photo, rating: rating) }}下一步会创建松弛切换从而将数据传给切换目标视图。这个方法必须标记为IBAction 属性,并使用一个segue(UIStoryboardSegue)作为参数。因为我们需要返回菜品列表场景,所以我们需要在MealTableViewController.swift中添加这个方法
我们需要实现的操作是往菜品列表中添加一个新的菜品,并且在视图中创建一个新的行来显示新的菜品
在MealTableViewController中添加方法
1、打开MealTableViewController.swift
2、在最后一个花括号前添加以下代码
@IBAction func unwindToMealList(sender: UIStoryboardSegue) {}
3、在方法中添加 if 语句
if let sourceViewController = sender.sourceViewController as? MealViewController, meal = sourceViewController.meal {}上述 if 语句的条件非常长。
上述代码使用了可选类型转换操作符(optional type cast operator)来将切换的来源视图控制器向下转为MealViewController类型。我们之所以需要向下转型是因为sender.sourceViewController是UIViewController类型,但是我们需要对MealViewController 类型进行操作。
操作符返回的是一个可选值,也就是如果向下转型失败我们会得到一个nil值。如果我们转型成功那么代码将把试图控制器赋值给本地 sourceViewController常量。并且检测是否为空。如果meal属性不为空那么我们将复制给本地的meal。从而执行 if 语句。
4、在 if 语句中添加下述代码
/ Add a new meal.let newIndexPath = NSIndexPath(forRow: meals.count, inSection: 0)
上述代码计算了新的菜品插入的位置,并且通过本地的 newIndexPath进行存储。
5、在 上述代码后添加
meals.append(meal)6、 在上述代码后添加
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
最终方法为:
@IBAction func unwindToMealList(sender: UIStoryboardSegue) { if let sourceViewController = sender.sourceViewController as? MealViewController, meal = sourceViewController.meal { // Add a new meal. let newIndexPath = NSIndexPath(forRow: meals.count, inSection: 0) meals.append(meal) tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom) }}
现在我们需要创建一个松弛的切换来触发这个方法
将保存按钮和 unwindToMealList 方法绑定
1、打开storyboard
2、使用Control + 拖拽的方式将 保存按钮拖拽到 Exit 项
当释放鼠标之后一个菜单会出现
3、选择 unwindToMealList 方法
现在当我们点击保存按钮的时候,导航按钮会返回菜品列表场景,同时unwindToMealList会被调用
里程碑:运行app。现在点击 + 按钮来创建一个新的菜品,然后点击保存。我们应该可以在菜品列表中看到新添加的菜品
当用户没有输入菜品名的时候让保存按钮不可用
因为在 MealViewController中meal 属性是一个可选的,如果没有名字的话我们的初始化器会返回一个nil,所以我们这个Meal 对象就不会被创建并添加到菜品列表中。只有当用户输入合法的名字的时候我们才让他们可以点击保存按钮
当没有菜品名的时候使保存按钮不可用
2、添加另一个UITextFieldDelegate 方法
func textFieldDidBeginEditing(textField: UITextField) { // Disable the Save button while editing. saveButton.enabled = false}
上述代码会在编辑看是的时候执行,或者当键盘显示的时候。这个代码使得保存按钮不可用
3、再添加一个方法
func checkValidMealName() { // Disable the Save button if the text field is empty. let text = nameTextField.text ?? "" saveButton.enabled = !text.isEmpty}这是一个助理方法,当文本框为空的时候使保存按钮不可用
4、找到 textFieldDidEndEditing 方法
func textFieldDidEndEditing(textField: UITextField) {}
5、添加以下代码
checkValidMealName()navigationItem.title = textField.text
第一行检测文本框是否有文本了,从而让保存按钮可用,第二行设置场景的标题为输入的文本
6、找到 viewDidLoad 方法
override func viewDidLoad() { super.viewDidLoad() // Handle the text field’s user input through delegate callbacks. nameTextField.delegate = self}
7、添加checkValidMealName方法。
// Enable the Save button only if the text field has a valid Meal name.checkValidMealName()
完整的vieDidLoad方法如下
override func viewDidLoad() { super.viewDidLoad() // Handle the text field’s user input through delegate callbacks. nameTextField.delegate = self // Enable the Save button only if the text field has a valid Meal name. checkValidMealName()}
里程碑:运行app,但点击 + 号的时候,直到我们输入合法的菜品名并且键盘消失的时候我们的保存按钮才会可用
取消添加一个新的菜品
用户可能会取消添加一个新的菜品,然后回到菜品列表。因此我们需要实现取消按钮的方法
1、打开storyboard
2、点击助手编辑
3、选择取消按钮
4、使用 Control + 拖拽将取消按钮从画布中拖到代码中
5、在出现的对话框中的Connection中选择Action
6、输入名字cancel
7、类型选择UIBarButtonItem
8、点击Connect
Xcode会添加必要的代码
@IBAction func cancel(sender: UIBarButtonItem) {}
9、在方法中添加
dismissViewControllerAnimated(true, completion: nil)
上述代码是的菜品场景消失,同时菜单列表会出现
最终的cancel代码为
@IBAction func cancel(sender: UIBarButtonItem) { dismissViewControllerAnimated(true, completion: nil)}
里程碑:运行app,现在点击 + 按钮之后点击取消按钮我们应该返回菜品列表
ps:完整的工程文件目录Download File
- iOS Apps 开发(Swift)(9)——Implement Navigation
- IOS Apps 开发(Swift)(6)——Implement a Custom Control(1)
- IOS Apps 开发(Swift)(6)——Implement a Custom Control(2)
- iOS Apps 开发(Swift)(10)——Implement Edit and Delete Behavior
- iOS Apps 开发(Swift)(11)——Persist Data
- IOS Apps 开发(Swift)(2)——Learn the Essentials of Swift(1)
- IOS Apps 开发(Swift)(2)——Learn the Essentials of Swift(2)
- IOS Apps 开发(Swift)(3)——Build a Basic UI(1)
- IOS Apps 开发(Swift)(3)——Build a Basic UI(2)
- IOS Apps 开发(Swift)(4)——Connect the UI to Code(1)
- IOS Apps 开发(Swift)(4)——Connect the UI to Code(2)
- IOS Apps 开发(Swift)(1)——Jump Right In
- IOS Apps 开发(Swift)(5)——Work with View Controllers
- IOS Apps 开发(Swift)(7)——Define Your Data Model
- IOS Apps 开发(Swift)(8)——Create a Table View
- iOS开发(Swift)——代理
- IOS开发学习笔记——Navigation学习
- ios开发——swift
- hdoj Clarke and MST 5627(求位运算and后得到的最大生成树)(并查集&位运算)好题
- Qt创建新文件
- 1078 Hashing
- 极客学院-通过"Android学习之Launcher源码学习"看UML序列图
- DatabaseError:database disk image is malformed的解决方法
- iOS Apps 开发(Swift)(9)——Implement Navigation
- UnicodeDecodeError: 'ascii' codec can't decode byte 0xb7 in position 7: ordinal not in range(128)
- Loadrunner重要概念——事务
- 在spark环境中运行demo的时候报错
- MSSQL数据库连接池
- [MEMO] Linux Kernel Debugging Training 琐碎(补充ing)
- 部分数学课件
- Centos环境下MongoDB安装流程
- uva 128 Software CRC