UIStackView

UIStackView 可以方便地创建竖直或水平的布局,自动管理大部分约束。更重要的是 ,UIStackView 支待嵌套 ,这种灵活的方式可以创建出非常好看的界面。

  1. 打开 Main. storyboard , 再从对象库中拖一个新的 UIViewController 到画布上。最后拖一个竖直的 UIStackView 到 UIViewController 的 视图上。给 UIStackView 添加 一些约束,让它距离上下左右各 8 点 ;

  2. 下面拖四个 UILabel 到 UIStackView 上。从上到下,将它们的文字改为 “Name (名字)”、"Serial (序列号)”、" Value (价值)”和 “Date Created (创建时间)“;

  • 现在马上出现了 一个问题:标签的边框都是红色的(表示有布局错误),并且有些界面有竖直布局不明确的警告。修复这个问题有两种方法:通过自动布局修复,或者使用 UIStackView 的属性修复 。首先使用自动布局修复。

隐藏的约束

  • 在第 3 章中学习过,每个视图都有一个内部内容大小,如果没有明确指定宽度或高度,视图就会使用内部内容大小来决定宽度或高度 。
  • 视图是使用隐藏的约束实现的。隐藏的约束是由视图的 内容变多优先级内容变少优先级 决定的。

内容变多优先级越高,越会保持视图不被拉伸。
内容变少优先级高的视图更能抵抗压缩,也就是不省略文字。

UIStack View 的分配

  1. 选择 Date Created (创建时间)标签,然后打开尺寸检视面板。找到 Vertical Content Hugging Priority(竖直内容变多优先级),把它降低到 249 。现在其他三个标签有更高的优先级,因此它们会和内容大小一致 ,而 Date Created 会撑满剩下的空间。

嵌套的 UIStack View

  • UIStackView 的一个强大特性是可以嵌套。接下来会在最大的 UIStackView 中嵌套几个 横向的 UIStackView,并且在上面的三个标签旁边各放置 一个 UITextField,用来展示 Item 对应的值,以及提供编辑功能。
  1. 选中画布上的 Name(名字)标签,点击自动布局菜单最后一个图标为“下载样式”的按钮 。这样会让选中的视图嵌套到 UIStackView 中 。

  2. 选中新的 UIStackView ,然后打开属性检视面板 。现在 UIStackView 是竖直方向的,但是这里需要 一个水平方向的。把 Axis (轴)属性的值改为 Horizontal (水平的)。

3.拖一个 UITextField 到 Name 标签的右边。默认情况下, UILabel 的内容变多优先级比 UITextField 的高,因此 UILabel 保持了内部内容的宽度,并且 UITextField 拉伸了。另一 方面,现在 UILabel 和 UITextField 有相同的内容变少优先级,当 UITextField 的文字太长时,就会出现布局不明确,打开 UITextField 的尺寸检视面板,把水平的 Content Compression Resistance Priority 设置为 749。

UIStack View 间距

现在 UILabel 和 UITextField 之间没有间隙,看起来非常拥挤 。 UIStackView 可以自定义元素之间的间距 。

  1. 选中水平的 UIStackView , 打开属性检视面板 ,将 Spacing ( 间距) 改为 8 点。 UITextField 为了适应间距而缩小了宽度,因为它的内容变少优先级比 UILabel 的低。
  • 界面还需要调整 一些细节:为竖直的 UIStackView 设笠 一 定间距;将 Date Created 标签文字设置为居中;将 Name、 Serial 和 Value 二个标签的宽度设置为相同 。
  1. 选中竖直的 UIStackView , 打开属性检视面板,将 Spacing 设置为 8 点;选中 Date Created 标签,打开属性检视面板,将 Alignment (对齐)改为 Center (居中)。 这样前两个问题就解决了 。
  • 虽然 UIStackView 大幅减少了界面上的约束,但有些约束仍然很重要 。 在当前界面中,由于标签的宽度不一样,所以 UITextField 的左侧并没有对齐 ( 英语中还不是很明显,如果本地化为其他语言,可能就更明显) 。 要解决这个问题,需要给三个 UITextField 设置开头约束 。
  1. 按住 Control,从名字 UITextField 拖到序列号 UITextField 上,再选择 Leading (开头)。然后对序列号 UITextField 和价值 UITextField 执行相同的操作 。
截屏2021-11-30 21.23.06.png

Segues

大部分 iOS 应用都有很多个 UIViewController , 用户可以在 UIViewController 之间切换 。 Storyboard 通过使用 segue, 不编写代码就可以实现这个交互。

  • segue 可以把另一个 UIViewController 的视图加到屏幕上,它是一个 UIStoryboardSegue 对象 。 每个 segue 都有一个样式 (style )操作对象 (action item)标识 (identifier)

样式:决定了 UIViewController 的展示方式;
操作对象:是 storyboard 文件中触发 segue 的视图对象,例如 UIButton 、 UITableViewCell 或者 UIControl ;
标识:是为了支持在代码中修改 segue, 这对于支持非视图控件触发的 segue 非常有用,例如可以通过摇晃手机触发,或者用 storyboard 无法添加的元素触发。

show seage (展示类型的 segue)

show segue 会根据要展示的内容来 决定如何展示 UIViewController 。

  • 现在需要实现一个 UITableViewController 和新的UIViewController 之间的 segue, 操作对象是 UITableViewCell(ItemCell) , 点击新的 UITableViewCell会显示 UIViewController 。

  • 在 Main. storyboard 中 选择 ItemsViewController 的 ItemCell 原型。按住 Control , 再拖到前面创建的 UIViewController 上,会出现选择 segue 样式的黑色菜单,选择 Show。


    截屏2021-11-30 20.14.02.png

从 UITableViewController 指向新的 UIViewController 的箭头,就是 segue 。箭头中的图标表示 segue 的类型是 show(展示)。每种 segue 都有一个唯一的图标。

绑定内容

要显示选中 Item 的内容 ,需要再创建 一个 UIViewController 子类。创建一个叫 DetailViewController 的 Swift 文件。

  1. 设置关于UITextField插座变量。(使用助手编辑器操作)
  • 打开 DetailViewController.swift 文件之后,按住 Option 再点击项目导航面板中的 Main. storyboard, 这样会在 DetailViewController. swift 旁边打开助手编辑器。


    截屏2021-11-30 20.25.20.png
  1. DetailViewController.swift 需要保存将要显示的 Item , 当视图加载后,需要根据Item 对象给每个 UITextField 设置合适的值。
import UIKit

class DetailViewController: UIViewController {
    @IBOutlet var nameField: UITextField!
    @IBOutlet var serialNumberField: UITextField!
    @IBOutlet var valueField: UITextField!
    @IBOutlet var dateLabel: UILabel!
    
    var item: Item!  //添加一个 Item 属性
    
    let numberFormatter: NumberFormatter = {  //使用闭包来实例化数字格式化程序
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal  //创建具有 .decimal 样式的 NumberFormatter,并将其配置为显示不超过2个小数位数。
        formatter.minimumFractionDigits = 2
        formatter.maximumFractionDigits = 2
        return formatter
    }()
    
    let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .none
        return formatter
    }()
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        nameField.text = item.name
        serialNumberField.text = item.serialNumber
//        valueField.text = "\(item.valueInDollars)"
//        dateLabel.text = "\(item.dateCreated)"
        
        valueField.text = numberFormatter.string(from: NSNumber(value: item.valueInDollars))  //调用 NumberFormatter方法 的固定格式,可以记住。
        dateLabel.text = dateFormatter.string(from: item.dateCreated)
    }
    

}

传递数据

当点击 UITableViewCell 之后,衙要 一种方式告诉 DetailViewController 选择了哪一个 Item 。

  • 当 segue 触发时, UIViewController 的prepare(for:sender: )会被调用 ,因此可以在这时传递数据。这个方法有两个参数: 一个是 UIStoryboardSegue, 表示哪个 segue 被触发了;另 一个是 sender , 表示哪个对象触发了segue(例如 UITableViewCell 、 UIButton 等)。
  1. 从 UIStoryboardSegue 可以获得三个信息 : 源 UIViewControl ler , 目标 UIViewController, 以及 segue 的标识 。 标识可以用来区分 segue, 下面给 segue 设置一个标识。
  • 打开 Main. storyboard , 点击两个 UIViewController 之间的箭头来选中 segue, 然后打开属性检视面板 。 在 Identifier (标识)中输入 Showltem
  1. 有了 segue 标识,就可以传递 Item 对象了。打开 ItemsViewController.swift, 实现prepare (for: sender:)。
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        switch segue.identifier {  //这里用switch语句来判断可能出现的标识。
        case "ShowItem"?:  //由于segue的标识是可选字符串类型的,因此在case末尾有一个问号。
            if let row = tableView.indexPathForSelectedRow?.row{  //找出点击了哪一行
                let item = itemStore.allItems[row]
                let detailViewController = segue.destination as! DetailViewController
                detailViewController.item = item
            }
        default:
            preconditionFailure("Unexpected segue identifier.")
            //捕获不能处理的标识,让应用崩溃,这样可以防止在编写程序的过程中,忘记设置 segue 的标识,也可以防止标识的拼写错误。
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容