Swift中面向协议的Segue Identifiers

8月份时候,我看了Swift in Practice WWDC15 Session这个视频,视频的第一部分给我的印象非常深刻,为此我写了一篇博文–A Beautiful Solution to Non-Optional UIImage Named in Swift。现在终于抽出时间针对视频的第二部分以及其他更多有趣的地方写篇文章了。

这篇文章是关于如何优雅地处理多个Segue Identifiers的。你猜对了–使用协议。

不幸的是,Segue Identifiers(跳转标识符)是基于字符串的硬编码。当你在Storyboard中添加它们的时候,在每一处相关的代码你都要把这串字符复制过去,拼写错误很可能就会发生。

@IBAction func onRedPillButtonTap(sender:AnyObject) {
    performSegueWithIdentifier("TheRedPillExperience", sender:self)
}

如果将来你决定更改segue identifier的名称,那你必须在硬编码字符串的每个地方进行修改…这就可能导致潜在的复制粘贴错误或者拼写错误的危险。

为了缓和这种情况,凡是在ViewController中使用1个以上segue identifier时,我都用枚举(enum)来解决这种问题。

enum SegueIdentifier: String {
    case TheRedPillExperience
    case TheBluePillExperience
}

但是那也会造成另外一系列的问题,主要是太过臃肿和丑陋

// ViewController.swift
@IBAction func onRedPillButtonTap(sender:AnyObject) {
    performSegueWithIdentifier(SegueIdentifier.TheRedPillExperience.rawValue, sender:self)
}

在prepareForSegue中处理时,问题会变得更加严重

这是我在Swift2.0之前真实的代码。现在你至少可以使用guard来处理,但是仍然不够理想:

override func prepareForSegue(segue:UIStoryboardSegue,sender:AnyObject?) {
    guard let identifier = segue.identifier,
        segueIdentifier = SegueIdentifier(rawValue:idnetifier) else {
            fatalError("Invalid segue identifier\(segue.identifier)")
    }
    switch segueIdentifier {
    case .TheRedPillExperience:
        print("��")
    case .TheRedPillExperience:
        print("��")
    }
}

无论如何,在整个APP的每一个视图控制器中,你都必须解决这个问题,那么你清除这个问题呢?如开篇提到的–使用协议来解决这个问题。

解决方案
这个解决方案非常优雅,不过并不是我想出来的。感谢苹果在15WWDC上关于架构的演讲。严格来讲,这些简直棒极了。

首先,你创建一个SegueHandlerType,它将SegueIdentifier枚举标识为一个类型:

import UIKit
import Foundation

protocol SegueHandlerType {
    typealias SegueIndentifier:RawRepresentable
}

现在你可以使用功能强大的协议扩展为UIViewController对象创建基于字符串的SegueIdentifier枚举:

// SegueHandlerType.swift
extension SegueHandlerType where Self: UIViewController, SegueIdentifier.RawValue == String 
{
    func performSegueWithIndentifier(segueIdentifier: SegueIdentifier, sender: AnyObject?) {
        performSegueWithIdentifier(segueIdentifier.rawValue, sender: sender)
    }
    func segueIdentifierForSegue(segue: UIStoryboardSegue)->SegueIdentifier {
        guard let identifier = segue.identifier,
            segueIdentifier = SegueIdentifier(rawValue:identifier) else {
                fatalError("Invalid segue identifier\(segue.identifier).")
            }
        return segueIdentifier
    }
}

注意在协议扩展中的方法并没有在协议中声明–它们并不表示被重写(overrider)。
这是我见过的最好的使用实例。现在,使用起来非常简单和美观

import UIKit

class ViewControllerUIViewControllersegueHandlerType {
    enum SegueIdentifier:String {
        case TheRedPillExperience
        case TheBluePillExperience
    }
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    override func prepareForSegue(segue:UIStoryboardSegue, sender:AnyObject?) {
        switch segueIdentifierForSegue(segue) {
            case .TheRedPillExperience:
                print("��")
            case .TheRedPillExperience:
                print("��")
        }
    }
    @IBAction func onRedPillButtonTap(sender:AnyObject) {
        performSegueWithIdentifier(.TheRedPillExperience, sender:self)
    }
}

结论

从视频中可以看到使用SegueHandlerType的好处包括:
当新添加segue没有被处理的时候,编译器就会报错
重用性
简洁的语法

我们同时也看到了协议的强大
使用协议以及关联类型来加强应用约束
通过一个被约束的协议扩展来共享实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值