swift-oc混编分享1-提示组件

软件环境:Xcode 13.2
创建时间:2022年 03月7号
适用范围:iOS项目

这篇文章都说了什么

  1. 在iOS中的toast、loading的简易实现
  2. 源码分享,快速在你的项目中搭建

学习目标:

快速度搭建提示信息,不用依赖复杂的三方


toast

效果图

swift-oc混编分享1-提示组件_第1张图片

swift-oc混编分享1-提示组件_第2张图片

 使用实例

// toast
HToast.show("不能为空")
// toast消失前禁止交互
HToast.show("请稍后", userEnable: false)

实现思路

在window上添加一个view,展示toast信息并自动延迟隐藏。
优点:不需要绑定view,方便在任何地方调用。
缺点:不适合多toast同时展示的需求(和elementUI、Android原生toast略有不同)

源码如下:

import UIKit

public class HToast: UIView {
    
    
    /// 展示toastView
    /// 默认允许用交互,展示时间2.0s
    @objc class public func show(_ title: String, userEnable: Bool = true) {
        
        UIApplication.shared.keyWindow?.statusView.isHidden = true

        let toastView = UIApplication.shared.keyWindow?.toastView
        toastView?.isUserInteractionEnabled = !userEnable
        
        // 前置
        UIApplication.shared.keyWindow?.bringSubviewToFront(toastView!)
        
        toastView?.show_in(title)
    }
    
    /// 展示toastView,允许用交互,展示时间2.50s
    @objc class public func show(_ title: String) {
        self.show(title, userEnable: true)
    }
    
    /// 手动强制隐藏
    @objc class public func hide() {
        let toastView = UIApplication.shared.keyWindow?.toastView
        toastView?.isHidden = true
        toastView?.alpha = 1
    }
    
    public override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = UIColor.black.withAlphaComponent(0.05)
        self.isUserInteractionEnabled = false
        
        addSubview(backView)
        backView.addSubview(titleLabel)
        backView.snp.makeConstraints { (make) in
            make.center.equalToSuperview()
        }
        titleLabel.snp.makeConstraints { (make) in
            make.top.equalTo(17)
            make.bottom.equalTo(-17)
            make.left.greaterThanOrEqualTo(20)
            make.right.greaterThanOrEqualTo(-20)
            make.width.lessThanOrEqualTo(kSCWIDTH - 90)
        }
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    // MARK: private
    
    // 记录自动隐藏的时间
    private var hideTime = ""

    @objc private func show_in(_ title: String) {
        
        let showTime = NSDate.init().timeIntervalSince1970
        
        self.isHidden = false
        titleLabel.text = title
        
        hideToastView(showTime)
    }
    
    /// 延迟隐藏toast,如果中间有其他toast刷新了隐藏时间,则以最后一次时间为准
    @objc private func hideToastView(_ interval: TimeInterval) {
        hideTime = "\(interval)"
        DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {
            if (self.hideTime == ("\(interval)")) {
                
                UIView.animate(withDuration: 1, animations: {
                    self.alpha = 0.01
                }, completion: { (finish) in
                    if finish {
                        self.isHidden = true
                        self.alpha = 1
                    }
                })
            }
        }
    }
    
    
    // MARK: views
    private lazy var backView: UIView = {
        let backView = UIView.init()
        backView.backgroundColor = UIColor.black.withAlphaComponent(0.6)
        backView.clipsToBounds = true
        backView.layer.cornerRadius = 8
        return backView
    }()
    
    private lazy var titleLabel: UILabel = {
        let lable = UILabel.init()
        lable.textAlignment = .center
        lable.font = mediumFont(16)
        lable.textColor = .white
        lable.numberOfLines = 3
        return lable
    }()
    
}


extension UIWindow {
    
    @objc var toastView :HToast {
        get {
            if let view = self.viewWithTag(123411980) {
                return view as! HToast
            } else {
                let view = HToast()
                view.tag = 123411980
                self.addSubview(view);
                view.snp.makeConstraints { (make) in
                    make.edges.equalToSuperview()
                }
                return view
            }
        }
    }
}

loading:

效果图

swift-oc混编分享1-提示组件_第3张图片

swift-oc混编分享1-提示组件_第4张图片

 使用实例

// 无标题,只转菊花
HStatus.show("")
// 有标题,转菊花,禁止交互
HStatus.show("上传中...", userEnable: false)
// 隐藏
 HStatus.hide()

实现思路

在window上添加一个view,展示loading信息,需要的时候可以隐藏。
优点:不需要绑定view,方便在任何地方调用。
重要功能:展示loading、展示进度、允许或者禁止loading期间交互、默认2s防抖

源码如下:


import UIKit

public class HStatus: UIView {
    
    var isUserEnabled = true
    
    /// 展示statusView,title不超过6个字,
    /// 可用于刷新文字,会重新布局
    /// 前2s禁止交互,默认2s后允许用交互 userEnable = false时一直不允许交互
    @objc class public func show (_ title: String, userEnable: Bool = true) {
        
        DispatchQueue.main.async {
            // 隐藏 toastView
            let window = UIApplication.shared.windows.first
            
            window?.toastView.isHidden = true
            
            // 找到status view
            let statusView = window?.statusView
            // 前置 防止遮挡
            window?.bringSubviewToFront(statusView!)
            
            statusView?.isUserEnabled = userEnable
            // 先禁止交互
            statusView?.isUserInteractionEnabled = true
            // 展示出来
            statusView?.show_in(title)
            // 2s后,检查是否允许交互
            statusView?.updateUserEnabled_in()
            
            var width = title.sizeWith(font: mediumFont(16), inSize: CGSize.init(width: 100, height: 20)).width;
            width = width + 25;
            var margin = (width - 32 - 18) / 3.0;
            
            // 未输入文字时,只展示菊花
            if title.count == 0 {
                margin = 10;
                statusView?.backView.snp.remakeConstraints { (make) in
                    make.center.equalToSuperview()
                    make.size.equalTo(CGSize.init(width: 56, height: 56))
                }
            }
            // 有文字时大小不小于80
            else if width < 80 {
                margin = (80 - 32 - 18) / 3.0;
                statusView?.backView.snp.remakeConstraints { (make) in
                    make.center.equalToSuperview()
                    make.size.equalTo(CGSize.init(width: 80, height: 80))
                }
            }
            
            // 按文字宽度展示
            else {
                statusView?.backView.snp.remakeConstraints { (make) in
                    make.center.equalToSuperview()
                    make.size.equalTo(CGSize.init(width: width, height: width))
                }
            }
            
            
            // 平均分割Y轴的空间(margin)
            statusView?.loadingImageView.snp.remakeConstraints { (make) in
                make.top.equalToSuperview().offset(margin)
                make.centerX.equalToSuperview()
            }
            
            statusView?.titleLabel.snp.remakeConstraints { (make) in
                make.bottom.equalTo(-margin)
                make.height.equalTo(18)
                make.centerX.equalToSuperview()
                make.left.equalTo(10)
                make.right.equalTo(-10)
            }
        }
    }
    
    /// 更新状态文字(不会重新布局)
    @objc class public func updateStatus (_ title: String) {
        DispatchQueue.main.async {
            let statusView = UIApplication.shared.windows.first?.statusView
            statusView?.titleLabel.text = title
        }
    }
    
    /// 隐藏状态
    @objc class public func hide () {
        let statusView = UIApplication.shared.windows.first?.statusView
        statusView?.stopAnimation_in();
    }
    
    // MARK: private
    @objc private func show_in(_ title: String) {
        titleLabel.text = title
        if self.isHidden {
            self.isHidden = false
            startAnimation()
        }
        
    }
    @objc private func stopAnimation_in() {
        DispatchQueue.main.async {
            self.loadingImageView.layer.removeAllAnimations()
            self.isHidden = true;
        }
    }
    
    @objc private func updateUserEnabled_in() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
            self.isUserInteractionEnabled = !self.isUserEnabled
        })
    }
    
    public override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = UIColor.clear
        self.isUserInteractionEnabled = false
        
        addSubview(backView)
        backView.addSubview(titleLabel)
        backView.addSubview(iconImageView)
        backView.addSubview(loadingImageView)
        
        iconImageView.snp.makeConstraints { (make) in
            make.center.equalTo(loadingImageView)
        }
        
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    @objc private func startAnimation() {
        let rotatinAnimation = CABasicAnimation.init(keyPath: "transform.rotation.z")
        rotatinAnimation.toValue = NSNumber.init(value: Double.pi * 2.0)
        rotatinAnimation.duration = 0.7
        rotatinAnimation.isCumulative = true
        rotatinAnimation.repeatCount = MAXFLOAT
        loadingImageView.layer.add(rotatinAnimation, forKey: "rotationAnimation")
    }
    
    // MARK: views
    private lazy var backView: UIView = {
        let backView = UIView.init()
        backView.backgroundColor = UIColor.white
        backView.layer.cornerRadius = 5
        backView.layer.setLayerShadow(.black.withAlphaComponent(0.16), offset: CGSize.init(width: 0, height: 0), radius: 16)
        return backView
    }()
    
    private lazy var titleLabel: UILabel = {
        let lable = UILabel.init()
        lable.textAlignment = .center
        lable.font = regularFont(12)
        lable.textColor = .black.withAlphaComponent(0.8)
        lable.numberOfLines = 3
        return lable
    }()
    
    private lazy var iconImageView: UIImageView = {
        let iconImageView = UIImageView.init(image: UIImage.init(named: "common_loading_icon"))
        return iconImageView
    }()
    
    private lazy var loadingImageView: UIImageView = {
        let imageView = UIImageView.init(image: UIImage.init(named: "common_loading_n"))
        return imageView
    }()
}


extension UIWindow {
    
    @objc var statusView :HStatus {
        get {
            if let view = self.viewWithTag(123411981) {
                return view as! HStatus
            } else {
                let view = HStatus()
                view.tag = 123411981
                view.isHidden = true
                self.addSubview(view);
                view.snp.makeConstraints { (make) in
                    make.edges.equalToSuperview()
                }
                return view
            }
        }
    }
}

总结/备注:

  1. toast默认展示2.5s,然后动画消失,可按需修改,或者扩展方法,自定义展示时间
  2. loading效果中,有两张图片,根据自己产品特色,自行设计,或换成系统的大菊花也行。
  3. 大部分情况下,网络发起/或者文件读写等耗时动作会开启loading,有结果之后,会toast提示结果,所以toast的时候,会自动隐藏loading,同样loading也会把正在展示的toast隐藏。根据需求不同可动态调整此逻辑,如果不需要关联,也可以把相关代码干掉。
  4. 布局用的snapKit
  5. 如果你的项目是纯OC的且不想混编,可以仿照思路写一个,代码不多。

没有什么组件是最好的,根据自己的业务不断的打磨,适合最重要。

你可能感兴趣的:(iOS,UI控件的使用,swift,ios,开发语言)