探索之旅:链式语法

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
查看查看352 回复回复3 收藏收藏 分享淘帖 转播转播 分享分享 微信
查看: 352|回复: 3
收起左侧

探索之旅:链式语法

[复制链接]
Ding 发表于 2016-10-25 10:45:55 | 显示全部楼层 |阅读模式
快来登录
获取最新的苹果动态资讯
收藏热门的iOS等技术干货
拷贝下载Swift Demo源代码
本帖最后由 Ding 于 2016-10-25 13:28 编辑

接触了自动布局的第三方库Masonry、Core BlueTooth的封装库BabyBlueTooth等,喜欢那些小圆点链式语法。
OC世界的方括号,嵌套多了就会像噩梦,适当地用用链式语法还是不错的。Swift本来就是点语法,但如果在适当的时候来点链式也能增加代码可读性。


有趣的东西!就以系统警示视图(有的框架里叫对话框)的使用为例探索一下。

目前开发的应用大多最低支持到iOS8,同时为了我们的探索更简单,就舍弃UIAlertView和UIActionSheet,只以UIAlertController为素材做我们的菜。(如果你的应用需要兼容iOS8之前的版本,也可以对UIAlertView和UIActionSheet做封装,让它们支持链式语法,不过要处理代理为闭包,稍微麻烦点。)

先来看UIAlertController的基本使用方式。假设我们要在某个UIViewController里展示一个这样的警示框:

探索之旅:链式语法

探索之旅:链式语法 - 敏捷大拇指 - 探索之旅:链式语法


就需要这样写了:
[Swift] 纯文本查看 复制代码
let alert = UIAlertController(title: "Title", message: "message", preferredStyle: .alert)
        alert.addTextField { textField in
            textField.placeholder = "Username"
        }
        alert.addTextField { textField in
            textField.placeholder = "Password"
            textField.isSecureTextEntry = true
        }
        let loginAction = UIAlertAction(title: "Login", style: .default) { action in
            if let textFields = alert.textFields {
                print("Username:\(textFields[0].text!)\nPassword:\(textFields[1].text!)")
            }
        }
        let cancelAction = UIAlertAction(title: "cacel", style: .cancel, handler: nil)
        alert.addAction(loginAction)
        alert.addAction(cancelAction)
        self.present(alert, animated: true, completion: nil)


用链式语法封装后呢?可以这样写:
[Swift] 纯文本查看 复制代码
self.alert(title: "Title", message: "message")
            .textField(configuration: { textField in
                textField.placeholder = "Username"
            })
            .textField(configuration: { textField in
                textField.placeholder = "Password"
                textField.isSecureTextEntry = true
            })
            .normalButton(title: "Login") { alert in
                if let textFields = alert.textFields {
                    print("Username:\(textFields[0].text!)\nPassword:\(textFields[1].text!)")
                }
            }
            .cancleButton(title: "cancle")
            .show(animated: true)


这就是我们的目标。

大体思路:我们写个UIViewController的扩展,用关联对象的方式添加几个属性,比如alertTitle、alertMessage甚至一个UIAlertController类型的属性,但是这样不很美。是的,有更好的方法。我们干脆写一个类,就叫ChainableAlert,这个类有那些属性,并支持链式语法,然后在UIViewControll的扩展里简单使用这个类即可。这样写还有个小小的好处:如果在一个非viewController的地方要展示警示框,可以直接用这个ChainableAlert来做(其实就是用UIApplication.shared.keyWindow?.rootViewController来做展示用的controller)。


在OC里实现点语法,要用上block(类似Swift里的闭包),但是在Swift里,简单多了。怎么做?不妨先闭上眼睛想一想,顺便休息下~

我们可以让方法返回实例自身!

其实到这里,本次探索已经在理论上结束了,不过我们还是走完实际的路吧。燃烧一把:
[Swift] 纯文本查看 复制代码
public typealias AlertButtonAction = (ChainableAlert) -> Void
public typealias AlertTextFieldConfigurationHandler = (UITextField) -> Void

@available (iOS 8, *)
public class ChainableAlert {
    
    // Mark: init
    
    /**
     Create an alert
     */
    public static func alert(title: String? = nil, message: String? = nil) -> ChainableAlert {
        return ChainableAlert(title: title, message: message, style: .alert)
    }
    /**
     Create an action sheet
     */
    public static func actionSheet(title: String? = nil, message: String? = nil) -> ChainableAlert {
        return ChainableAlert(title: title, message: message, style: .actionSheet)
    }
    
    // Mark: add buttons
    
    /**
     Add a normal button to the alert
     */
    public func normalButton(title: String, handler: AlertButtonAction? = nil) -> ChainableAlert {
        let entity = AlertButtonEntity(title: title, action: handler == nil ? {_ in} : handler!)
        if normalEntities == nil {
            normalEntities = []
        }
        normalEntities?.append(entity)
        return self
    }
    
    /**
     Add a destructive button to the alert
     */
    public func destructiveButton(title: String, handler: AlertButtonAction? = nil) -> ChainableAlert {
        let entity = AlertButtonEntity(title: title, action: handler == nil ? {_ in} : handler!)
        if destructiveEntities == nil {
            destructiveEntities = []
        }
        destructiveEntities?.append(entity)
        return self
    }
    
    /**
     Add a cancel button to the alert, the most number of cancel button is 1
     */
    public func cancleButton(title: String, handler: AlertButtonAction? = nil) -> ChainableAlert {
        let entity = AlertButtonEntity(title: title, action: handler == nil ? {_ in} : handler!)
        cancleEntity = entity
        return self
    }
    
    /**
     Add a textField to the alert, if is action sheet, no use.
     */
    public func textField(configuration: AlertTextFieldConfigurationHandler? = nil) -> ChainableAlert {
        guard style == .alert else {
            return self
        }
        let handler = configuration == nil ? {_ in } : configuration!
        if textHandlers == nil {
            textHandlers = []
        }
        textHandlers?.append(handler)
        return self
    }
    
    /// textFields added to the alert.
    public var textFields: [UITextField]?
    
    // Mark: show
    
    /**
     Show the alert
     - parameter viewController: We will use it to present
     - parameter fromPosition:   If is action sheet, and device is iPad, we can set the source point for the popover controller
     */
    public func show(viewController: UIViewController? = nil, animated:Bool, fromPosition:(x: CGFloat, y: CGFloat)? = nil, completion: (() -> Void)? = nil) {
        let alertStyle: UIAlertControllerStyle = style == .alert ? .alert : .actionSheet
        let alertController = UIAlertController(title: title, message: message, preferredStyle: alertStyle)
        
        if let entities = normalEntities {
            for entity in entities {
                let action = UIAlertAction(title: entity.title, style: .default) { action in
                    let buttonAction = entity.action
                    buttonAction(self)
                }
                alertController.addAction(action)
            }
        }
        
        if let entities = destructiveEntities {
            for entity in entities {
                let action = UIAlertAction(title: entity.title, style: .destructive) { action in
                    let buttonAction = entity.action
                    buttonAction(self)
                }
                alertController.addAction(action)
            }
        }
        
        if let entity = cancleEntity {
            let action = UIAlertAction(title: entity.title, style: .cancel) { action in
                let buttonAction = entity.action
                buttonAction(self)
            }
            alertController.addAction(action)
        }
        
        if let textHandlers = textHandlers {
            for handler in textHandlers {
                alertController.addTextField(configurationHandler: handler)
            }
            textFields = alertController.textFields
        }
        
        func showWithViewController(controller: UIViewController) {
            if let popoverController = alertController.popoverPresentationController {
                popoverController.sourceView = controller.view
                
                if let fromPosition = fromPosition {
                    popoverController.sourceRect = CGRect(x: fromPosition.x, y: fromPosition.y, width: 0, height: 0)
                } else {
                    let size = controller.view.bounds.size
                    popoverController.sourceRect = CGRect(x: size.width/2, y: size.height - 2, width: 0, height: 2)
                }
            }
            controller.present(alertController, animated: animated, completion: completion)
        }
        
        if let controller = viewController {
            showWithViewController(controller: controller)
        } else if let controller = UIApplication.shared.keyWindow?.rootViewController {
            showWithViewController(controller: controller)
        }
    }
    
    // Mark: private
    
    fileprivate var title: String?
    fileprivate var message: String?
    fileprivate var style: AlertStyle = .alert
    
    fileprivate var normalEntities: [AlertButtonEntity]?
    fileprivate var destructiveEntities: [AlertButtonEntity]?
    fileprivate var cancleEntity: AlertButtonEntity?
    fileprivate var textHandlers: [AlertTextFieldConfigurationHandler]?
    
    private init(title: String?, message: String?, style: AlertStyle) {
        self.title = title
        self.message = message
        self.style = style
    }
    
}

// Mark: - private

private extension ChainableAlert {
    
    enum AlertStyle : Int {
        case alert
        case actionSheet
    }
    
    struct AlertButtonEntity {
        var title: String
        var action: AlertButtonAction
    }
}


在UIViewController的扩展文件里:
[Swift] 纯文本查看 复制代码
@available (iOS 8, *)
public extension UIViewController {
    
    /**
     Create an alert
     */
    public func alert(title: String? = nil, message: String? = nil) -> UIViewController {
        chainableAlert = ChainableAlert.alert(title: title, message: message)
        return self
    }
    /**
     Create an action sheet
     */
    public func actionSheet(title: String? = nil, message: String? = nil) -> UIViewController {
        chainableAlert = ChainableAlert.actionSheet(title: title, message: message)
        return self
    }
    
    // Mark: add buttons
    
    /**
     Add a normal button to the alert
     */
    public func normalButton(title: String, handler: AlertButtonAction? = nil) -> UIViewController {
        let _ = chainableAlert?.normalButton(title: title, handler: handler)
        return self
    }
    
    /**
     Add a destructive button to the alert
     */
    public func destructiveButton(title: String, handler: AlertButtonAction? = nil) -> UIViewController {
        let _ = chainableAlert?.destructiveButton(title: title, handler: handler)
        return self
    }
    
    /**
     Add a cancel button to the alert, the most number of cancel button is 1
     */
    public func cancleButton(title: String, handler: AlertButtonAction? = nil) -> UIViewController {
        let _ = chainableAlert?.cancleButton(title: title, handler: handler)
        return self
    }
    
    /**
     Add a textField to the alert, if is action sheet, no use.
     */
    public func textField(configuration: AlertTextFieldConfigurationHandler? = nil) -> UIViewController {
        let _ = chainableAlert?.textField(configuration: configuration)
        return self
    }
    
    // Mark: show
    
    /**
     Show the alert
     - parameter fromPosition:   If is action sheet, and device is iPad, we can set the source point for the popover controller
     */
    public func show(animated: Bool, fromPosition: (x: CGFloat, y: CGFloat)? = nil, completion: (() -> Void)? = nil) {
        if let alert = chainableAlert {
            alert.show(viewController: self, animated: animated, fromPosition: fromPosition) {
                completion?()
            }
        }
    }
}

private extension UIViewController {
    struct AssociatedKeys {
        static var chainableAlertKey = "chainableAlertKey"
    }
    var chainableAlert: ChainableAlert? {
        get {
            return (objc_getAssociatedObject(self, &AssociatedKeys.chainableAlertKey) as? ChainableAlert)
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.chainableAlertKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}



最后还是说一句:爱起来,用起来,做起来~


写着写着,慢慢就多起来,应阿牛哥之请,弄了个海淘贴:探索之旅。可以去订阅。








更多图片 小图 大图
组图打开中,请稍候......

都看到这里了,就把这篇资料推荐给您的好朋友吧,让他们也感受一下。

回帖是一种美德,也是对楼主发帖的尊重和支持。

*声明:敏捷大拇指是全球最大的Swift开发者社区、苹果粉丝家园、智能移动门户,所载内容仅限于传递更多最新信息,并不意味赞同其观点或证实其描述;内容仅供参考,并非绝对正确的建议。本站不对上述信息的真实性、合法性、完整性做出保证;转载请注明来源并加上本站链接,敏捷大拇指将保留所有法律权益。如有疑问或建议,邮件至marketing@swifthumb.com

*联系:微信公众平台:“swifthumb” / 腾讯微博:@swifthumb / 新浪微博:@swifthumb / 官方QQ一群:343549891(满) / 官方QQ二群:245285613 ,需要报上用户名才会被同意进群,请先注册敏捷大拇指

嗯,不错!期待更多好内容,支持一把:
支持敏捷大拇指,用支付宝支付10.24元 支持敏捷大拇指,用微信支付10.24元

评分

参与人数 1金钱 +10 贡献 +10 专家分 +10 收起 理由
Anewczs + 10 + 10 + 10 32个赞!专家给力!

查看全部评分

本帖被以下淘专辑推荐:

swifter 发表于 2016-10-25 10:59:14 | 显示全部楼层
爱起来,用起来,做起来~


相见不如怀念 发表于 2016-10-26 01:20:36 | 显示全部楼层
赞!

要是有办法让大多数类也可以轻松支持点语法就好了
 楼主| Ding 发表于 2016-10-26 08:07:23 | 显示全部楼层
相见不如怀念 发表于 2016-10-26 01:20
赞!

要是有办法让大多数类也可以轻松支持点语法就好了

所以,没事就用Swift吧,天生点语法。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

做任务,领红包。
我要发帖

分享扩散

都看到这里了,就把这资料推荐给您的好朋友吧,让他们也感受一下。
您的每一位朋友访问此永久链接后,您都将获得相应的金钱积分奖励
热门推荐

合作伙伴

Swift小苹果

  • 北京治世天下科技有限公司
  • ©2014-2016 敏捷大拇指
  • 京ICP备14029482号
  • Powered by Discuz! X3.1 Licensed
  • swifthumb Wechat Code
  •   
快速回复 返回顶部 返回列表