Swift-核心之面向协议开发

Swift is a Protocol-Oriented Programming Language

Swift 是一门面向协议 (POP) 开发的语言

Swift 的核心是面向协议编程

WWDC 对 OOP 很好的诠释:

Protocol-Oriented Programming in Swift


POP 面向协议的编程

面向协议的编程的核心是抽象(Abstraction)和简化(Simplicity)
协议的高级使用是协议的延展
协议(protocol) + 结构体(struct) > 类(class)

面向对象面向协议比较
  • 面向对象是一个很古老的软件开发模式,通过类来实现
  • 面向协议是苹果在 swift 中主推的,通过协议和结构体,可以代替类
  • Swift 中的很多对象都改成了结构体和协议
  • 并不是所有的类都可以被协议+结构体替代,但大多数是可以被替换的
  • 面向协议使代码更加灵活,类似于组件化开发,符合工厂方法模式

实例比较:给一个类添加额外的方法
  • 通过继承
    • 创建一个继承类的子类,在子类中添加方法,以后使用子类即可获取这个方法
  • 通过协议
    • 为这个方法定义一个协议,那个类需要实现这个方法,协议即可
使用继承的缺点:
  • 通过继承添加的方法,不一定每个子类都会使用,使代码冗余
  • 拥有太多的子类,使类冗余
  • 对于父类的方法太过依赖,当父类的方法更改后,影响子类的重载方法

什么时候使用面向对象的类呢?


协议的高级使用是协议的延展以及和结构体配合
为协议添加属性, 属性为可读或可写
protocol MessageModelProtocol {
    var name: String {get set}
    var age:  Int { set get }
}
定义一个接受协议的结构体
struct MessageModel: MessageModelProtocol {

    var name: String = ""
    var age: Int = 0
    var isMale: Bool = false

    init(with dict: [String: Any]) {
        self.name = (dict["name"] as? String) ?? ""
        self.age = (dict["age"] as? Int) ?? 0
        self.isMale = (dict["isMale"] as? Bool) ?? false
    }
}
对协议进行延展
extension MessageModelProtocol {
    mutating func test() {
        self.name = "Hello iPhone 8"
    }
}
协议的协议
protocol DemoMessageModelProtocol: MessageModelProtocol {
    var date: Date { set get }
}

具体实例运用:
  • 给 UIViewController 添加 一个数据为空视图
  • 给 UIViewController 添加 一个遮挡提示视图
  • 给 xib 添加一个快速获取示例方法
  • ……(对控制器依赖比较小的视图等)

数据为空或者网络请求失败提示界面

这里写图片描述 这里写图片描述


import UIKit

enum EmptyType {
    case emptyData
    case networkError
}

protocol EmptyDataSetProtocol { }

extension EmptyDataSetProtocol where Self : UIViewController {

    func addEmptyView(type: EmptyType? = .emptyData, iconName: String, tipTitle: String, action: Selector? = nil) {

        let emptyView = UIView(frame: view.bounds)
        emptyView.backgroundColor = UIColor.white
        emptyView.tag = 1024
        view.addSubview(emptyView)

        let icomViewW: CGFloat = 100
        let imageView = UIImageView(image: UIImage(named: iconName))
        imageView.frame.size = imageView.image?.size ?? CGSize(width: icomViewW, height: icomViewW)
        imageView.contentMode = .center
        imageView.center = CGPoint(x: emptyView.center.x, y: emptyView.center.y - 100)
        emptyView.addSubview(imageView)

        let tipLabel = UILabel()
        let margin: CGFloat = 20
        tipLabel.numberOfLines = 0
        tipLabel.font = UIFont.systemFont(ofSize: 14)
        tipLabel.textColor = UIColor.lightGray

        if tipTitle.contains("\n") {
            let style = NSMutableParagraphStyle()
            style.lineSpacing = 5 // 设置行间距
            style.alignment = .center // 文字居中
            let tipString = NSAttributedString(string: tipTitle, attributes: [NSParagraphStyleAttributeName: style])
            tipLabel.attributedText = tipString
        } else {
            tipLabel.text = tipTitle
        }
        tipLabel.adjustsFontSizeToFitWidth = true
        tipLabel.textAlignment = .center
        tipLabel.sizeToFit()
        tipLabel.frame = CGRect(x: margin, y: imageView.frame.maxY + margin, width: UIScreen.main.bounds.width - margin*2, height: tipLabel.bounds.height)
        emptyView.addSubview(tipLabel)

        // 网络请求失败
        if type == .networkError {

            let reloadButton = UIButton(type: .system)
            reloadButton.frame.size = CGSize(width: 100, height: 36)
            reloadButton.center = CGPoint(x: emptyView.center.x, y: tipLabel.frame.maxY + margin*2)
            reloadButton.backgroundColor = UIColor(red: 255/255.0, green: 42/255.0, blue: 102/255.0, alpha: 1.0)
            reloadButton.layer.cornerRadius = 18
            reloadButton.layer.masksToBounds = true
            reloadButton.setTitle("重新加载", for: .normal)
            reloadButton.setTitleColor(UIColor.white, for: .normal)
            reloadButton.titleLabel?.font = UIFont.systemFont(ofSize: 16)
            reloadButton.addTarget(self, action: action!, for: .touchUpInside)
            emptyView.addSubview(reloadButton)
        }

    }

    func hideEmptyView() {
        view.subviews.filter({ $0.tag == 1024 }).first?.removeFromSuperview()
    }
}
具体使用:
class ViewController: UIViewController, EmptyDataSetProtocol {...}
/// 显示数据为空视图
func showEmptyDataView() {
    addEmptyView(type: .emptyData, iconName: "emptyData", tipTitle: "数据为空")
}

/// 显示请求失败重新加载视图
func showNetworkErrorReloadView() {
    addEmptyView(type: .networkError, iconName: "network_error", tipTitle: "网络出问题了,请检查网络", action: #selector(reloadData))
}

/// 移除空视图/重新加载视图
func removeEmptyView() {
    hideEmptyView()
}

添加 guide 视图 到 window 上

这里写图片描述

import UIKit

protocol GuideViewProtocol { }

extension GuideViewProtocol where Self : UIViewController {

    func showGuideView(with title: String, imageName: String, buttonName: String, sureAction: Selector, cancelAction: Selector) {

        let kScreenW: CGFloat = UIScreen.main.bounds.width
        let kMargine: CGFloat = 30

        let backgroundView = UIView(frame: UIScreen.main.bounds)
        backgroundView.backgroundColor = UIColor.black.withAlphaComponent(0.3)
        backgroundView.tag = 1024
        UIApplication.shared.keyWindow?.addSubview(backgroundView)

        let containerView = UIView()
        containerView.frame.size = CGSize(width: kScreenW-kMargine*2, height: (kScreenW-kMargine*2) + 20)
        containerView.backgroundColor = UIColor.white
        containerView.center = backgroundView.center
        containerView.layer.cornerRadius = 15
        backgroundView.addSubview(containerView)

        let tipLabel = UILabel(frame: CGRect(x: kMargine, y: 30, width: containerView.bounds.width-kMargine*2, height: kMargine))
        tipLabel.font = UIFont.systemFont(ofSize: 20)
        tipLabel.textColor = UIColor.red
        tipLabel.textAlignment = .center
        tipLabel.text = title
        containerView.addSubview(tipLabel)

        let sureButton = UIButton(type: .system)
        sureButton.frame.size = CGSize(width: 200, height: 30)
        sureButton.setTitle(buttonName, for: .normal)
        sureButton.setTitleColor(UIColor.white, for: .normal)
        sureButton.backgroundColor = UIColor.red
        sureButton.titleLabel?.font = UIFont.systemFont(ofSize: 18)
        sureButton.frame.origin.x = (containerView.bounds.width - sureButton.bounds.width)*0.5
        sureButton.center.y = containerView.bounds.height - 20 - sureButton.bounds.height
        sureButton.layer.cornerRadius = 15
        sureButton.addTarget(self, action: sureAction, for: .touchUpInside)
        containerView.addSubview(sureButton)

        let centerImageView = UIImageView(image: UIImage(named: imageName))
        centerImageView.contentMode = .scaleAspectFit
        centerImageView.frame = CGRect(x: 30, y: tipLabel.frame.maxY+20, width: containerView.bounds.width-60, height: sureButton.frame.minY - tipLabel.frame.maxY - 40)
        containerView.addSubview(centerImageView)

        let cancelButton = UIButton(type: .custom)
        cancelButton.setBackgroundImage(UIImage(named: "cancelButton"), for: .normal)
        cancelButton.frame.size = cancelButton.currentBackgroundImage?.size ?? CGSize.zero
        cancelButton.center.x = UIScreen.main.bounds.width * 0.5
        cancelButton.center.y = containerView.frame.maxY + 50
        cancelButton.addTarget(self, action: cancelAction, for: .touchUpInside)
        backgroundView.addSubview(cancelButton)

    }

    func hideGuideView() {
        UIView.animate(withDuration: 0.25) {
            UIApplication.shared.keyWindow?.subviews.filter({ $0.tag == 1024 }).first?.removeFromSuperview()
        }
    }
}

具体使用
class ViewController: UIViewController, GuideViewProtocol {...}
/// 添加指示视图到当前视图的 window 上
func addGuideView() {
    showGuideView(with: "您有VIP礼包待领取", imageName: "vip_image", buttonName: "立即领取", sureAction: #selector(sureAction), cancelAction: #selector(removeGuideView))
}

/// 提示视图上按钮的点击事件
func sureAction() {
    show(FirstViewController(), sender: nil)
    hideGuideView()
}

/// 移除指示视图
func removeGuideView() {
    hideGuideView()
}

修改导航栏左侧按钮
protocol LeftBarButtonChangeable { }

extension LeftBarButtonChangeable where Self : UIViewController {

    func changeLeftBarButton(_ imageName: String, action: Selector ) {
        let itemButton = UIButton(type: .custom)
        itemButton.setImage(UIImage(named: imageName), for: .normal)
        itemButton.sizeToFit()
        itemButton.addTarget(self, action: action, for: .touchUpInside)
        self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: itemButton)
    }
}
具体使用
class ViewController: UIViewController, LeftBarButtonChangeable {

    override func viewDidLoad() {
        super.viewDidLoad()

        changeLeftBarButton("back_image", action: #selector(backAction))
    }

    func backAction() {
        navigationController?.popViewController(animated: true)
    }
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值