Xcode11 新特性之SegueAction

Xcode11发布后,我们一直在惊叹SwiftUI的强大,却忽略了storyboard的一些改进。据我所知,AppleWWDC期间也没有提到过segue自定义初始化(initializers)的修改。 我们可以从Xcode和iOS 13发行说明中看到一些修改提示。

SegueAction

假设我们使用storyboards时通过Segue进行页面跳转

001

左侧的BookController有一个Button,点击该按钮时,它会跳转到右侧的PreviewController,以显示该Book的详情。 使用storyboard,您可以通过从按钮拖拽到目标控制器创建segue实现页面跳转。

002

要完成segue的配置,必须在属性检查器( attributes inspector)中添加一个唯一标识符(Identifier):

003

页面跳转时,我们需要将Model数据(本例中的Book)从源传递到目标ViewController。 在Xcode 10和iOS 12中,我们使用源视图控制器中的 prepare(for:sender) 来实现:

// BookController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  guard let previewController = segue.destination as? PreviewController else {
    fatalError("Missing PreviewController")
  }
  previewController.book = book
}

注意,我们没有创建目标视图控制器。 UIKit通过调用init(coder:) 方法来初始化新的ViewController。 我们可以配置数据并将其传递给controller,但只能在UIKit创建controller之后。

我的PreviewController有一个Book属性,但它是可选的:

// PreviewController
import UIKit

final class PreviewController: UIViewController {
  @IBOutlet private var textView: UITextView!

  var book: Book?

  override func viewDidLoad() {
    super.viewDidLoad()
    title = book?.title
    textView.text = book?.preview
  }
}

book属性是optional的,因为在初始化PreviewController期间无法设置它。我想在创建PreviewController时初始化book,并把book成员变量改成let不可修改的,怎么办呢?

segue是UIKit在调用segue方法期间,创建目标视图控制器的一种方法。

从Xcode 11开始,我们还有另一种将数据传递到目标视图控制器的方法。我们可以通过使用@IBSequeAction在源视图控制器中标记一个方法来创建segue action

@IBSegueAction
private func showPreview(coder: NSCoder, sender: Any?, segueIdentifier: String?)
    -> PreviewController? {
    return PreviewController(coder: coder, book: book)
}

SegueAction方法具有三个参数。 必需的NSCoder参数以及可选的sender和segueIdentifier。 如果不需要,我们可以省略可选参数:

@IBSegueAction
private func showPreview(coder: NSCoder)
    -> PreviewController? {
    return PreviewController(coder: coder, book: book)
}

如果该方法返回nil,则UIKit将调用 init(coder:) 方法来创建视图控制器。 SegueAction不会阻止segue的发生。 无论哪种方式,都会在segue对象中传递新创建的视图控制器给prepare(for:sender)方法。 由于这里我们不再需要它,因此从视图控制器中删除了prepare(for:sender)方法。

注意,Swift 5.1允许我们省略具有单个表达式的方法的return语句,我们进一步简化SegueAction方法:

@IBSegueAction
private func showPreview(coder: NSCoder)
    -> PreviewController? {
    PreviewController(coder: coder, book: book)
}

要将storyboard中的segue连接到ViewController中的SegueAction方法,请从segue对象向view controller拖动,然后选择SegueAction方法:

004
0041

如果正确连接了SegueAction,在segue的属性检查器(attributes inspector)中会看到该方法的Selector变成了你定义的方法:

005

现在,PreviewController可以创建一个自定义initializer方法,该初始化方法在创建视图控制器时 用我们SegueAction传递的Book做参数:

// PreviewController
import UIKit
final class PreviewController: UIViewController {
  @IBOutlet private var textView: UITextView!

  let book: Book

  init?(coder: NSCoder, book: Book) {
    self.book = book
    super.init(coder: coder)
  }

  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    title = book.title
    textView.text = book.preview
  }
}

自定义初始化方法必须调用super.init(coder:) 并传递从SegueAction中接收到的coder参数。 另外,Book属性不再是可选的,因此我们可以将其从var更改为let

自定时实例化方法

页面跳转时我们可以使用storyboards而不必使用segue。 例如,我可以将按钮连接到视图控制器中的IBAction方法,而不是创建segue:

@IBAction private func showPreview(_ sender: UIButton) {
  guard let previewController = storyboard?.instantiateViewController(
      withIdentifier: "PreviewController") as? PreviewController else {
      fatalError("Unable to create PreviewController")
    }
    previewController.book = book
    show(previewController, sender: self)
}

showPreview方法从storyboard中实例化视图控制器,进行配置然后显示。 这种方式存在与segue相同的一些问题。 我们调用storyboard的instantiateViewController(withIdentifier:)方法,然后返回一个ViewController。 任何Model数据(例如我们的Book)都必须是目标视图控制器的可选属性。

苹果在iOS 13发行说明中对这个问题进行了完善:

You can now invoke a custom initializer from a creation block that’s passed through instantiateInitialViewController(creator:) or instantiateViewController(identifier:creator:).

例如,使用我们的自定义初始化方法传递Model数据:

@IBAction private func showPreview(_ sender: UIButton) {
  guard let previewController = storyboard?.instantiateViewController(
        identifier: "PreviewController", 
        creator: { coder in
        PreviewController(coder: coder, book: self.book)
      }) else {
      fatalError("Unable to create PreviewController")
    }
    show(previewController, sender: self)
}

storyboard.instantiateViewController多了一个creator参数,creator block提供了我们需要传递给自定义view controller初始化方法的coder,现在就把book可选参数改成let了。

迟到总比没有好

未来可能是SwiftUI一统天下,但我仍然很高兴storyboard有所完善。 第一个改动使用Xcode11开发低版本iOS也可以使用,第二个改动就需要iOS13及以上版本支持了。 所以也许这就是迟到总比没有好

作者:kharrison
翻译整理:乐Coding
原文地址:https://useyourloaf.com/blog/better-storyboards-with-xcode-11/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容