美文网首页
iOS 镂空 页面引导

iOS 镂空 页面引导

作者: 小白lf | 来源:发表于2021-10-22 11:06 被阅读0次

    直接上代码

    import Foundation
    import SnapKit
    import UIKit
    
    // MARK: - GuideContainerView
    
    /// 镂空位置
    struct GuideLocation {
        struct Rect {
            /// 镂空尺寸
            var frame: CGRect
    
            /// 镂空区域圆角半径
            var cornerRadius: CGFloat = 0
    
            /// 镂空区域水平间距
            var dx: CGFloat = -1
    
            /// 镂空区域垂直间距
            var dy: CGFloat = -1
    
            var bPath: UIBezierPath {
                return UIBezierPath(roundedRect: frame.insetBy(dx: dx, dy: dy), cornerRadius: cornerRadius)
            }
        }
        
        var rects: [Rect] = []
        
        var paths: [UIBezierPath] = []
    }
    
    /// CoverView 的 子控件布局参考点位
    struct Point {
        var minX: CGFloat = 0
        var minY: CGFloat = 0
        var maxX: CGFloat = 0
        var maxY: CGFloat = 0
    }
    
    /// 引导view协议
    protocol CoverViewProtocol: UIView {
        func layout(make: ConstraintMaker, super: UIView)
    
        /// 镂空位置
        var location: GuideLocation { get set }
    
        /// 供 接入方 使用
        var clickAction: ((@escaping (Bool) -> Void) -> Void)? { get set }
    
        /// 供 GuideContainerView 使用
        var nextStep: (() -> Void)? { get set }
        
        /// 子控件布局参考点位
        var referPoint: Point { get set }
    }
    
    /// 引导容器
    class GuideContainerView: BaseView {
        public var covers: [CoverViewProtocol] = []
        
        /// 所有 引导图 展示完毕
        public var complete: (() -> Void)?
    
        private var index: Int = 0
        
        deinit {
            print("GuideContainerView deinit")
        }
        
        override func setupUI() {
            super.setupUI()
            clipsToBounds = true
            backgroundColor = UIColor.black.withAlphaComponent(0.7)
        }
    
        public func show(in view: UIView? = UIApplication.shared.keyWindow) {
            guard let view = view else { return }
            if covers.isEmpty { return }
            index = 0
            view.addSubview(self)
            snp.makeConstraints { make in
                make.edges.equalToSuperview()
            }
    
            setNeedsLayout()
            layoutIfNeeded()
    
            showCover()
        }
    
        private func showCover() {
            if index < 0 || index > covers.count - 1 {
                complete?()
                removeFromSuperview()
                return
            }
    
            let cover = covers[index]
            cover.nextStep = { [weak self, weak cover] in
                guard let self = self else { return }
                guard let cover = cover else { return }
                cover.removeFromSuperview()
                self.index += 1
                self.showCover()
            }
    
            addSubview(cover)
            cover.snp.makeConstraints { [weak self] make in
                guard let self = self else { return }
                cover.layout(make: make, super: self)
            }
    
            let path = UIBezierPath(rect: bounds)
            if cover.location.rects.isNotEmpty {
                for rect in cover.location.rects {
                    path.append(rect.bPath)
                }
            }
            
            if cover.location.paths.isNotEmpty {
                for subPath in cover.location.paths {
                    path.append(subPath)
                }
            }
    
            let shape = CAShapeLayer()
            shape.path = path.cgPath
            shape.fillRule = .evenOdd
            layer.mask = shape
        }
    }
    
    // MARK: - CoverView
    
    class CoverView: BaseView, CoverViewProtocol {
        var clickAction: ((@escaping (Bool) -> Void) -> Void)?
        
        var nextStep: (() -> Void)?
    
        var location = GuideLocation()
        
        var referPoint = Point()
    
        func layout(make: ConstraintMaker, super: UIView) {
            make.edges.equalToSuperview()
        }
    }
    
    
    

    举个🌰

    1.自定义 CoverView

    class HomeFinanceCoverView: CoverView {
        enum Step {
            case s1
        }
        
        var step: Step = .s1
        
        let imgStep = UIImageView().then {
            $0.isUserInteractionEnabled = true
        }
        
        let imgGuide = UIImageView()
        
        deinit {
            print("HomeFinanceCoverView deinit")
        }
        
        override func setupUI() {
            super.setupUI()
            
            addSubview(imgStep)
            addSubview(imgGuide)
            
            imgStep.rx.tapGesture().when(.ended)
                .subscribe(onNext: { [weak self] _ in
                    guard let self = self else { return }
                    if let clickAction = self.clickAction {
                        clickAction { finish in
                            if finish {
                                self.nextStep?()
                            }
                        }
                    } else {
                        self.nextStep?()
                    }
                })
                .disposed(by: rx.disposeBag)
        }
        
        override func didMoveToSuperview() {
            super.didMoveToSuperview()
    
            switch step {
            case .s1:
                imgGuide.image = UIImage(named: "loan_guide_1")
                imgStep.image = UIImage(named: "loan_step_1")
                
                imgGuide.snp.makeConstraints { make in
                    make.leading.equalTo(16.shtScale)
                    make.size.equalTo(CGSize(width: 178.shtScale, height: 106.shtScale))
                    make.top.equalTo(self.referPoint.maxY + 8.shtScale)
                }
                
                imgStep.snp.makeConstraints { make in
                    make.centerX.equalToSuperview()
                    make.size.equalTo(CGSize(width: 106.shtScale, height: 56.shtScale))
                    make.bottom.equalToSuperview().offset(-40.shtScale)
                }
            }
        }
    }
    

    2.使用

      let cover = GuideContainerView()
      let cell = ...
      let rect = collectionView.convert(cell.frame, to: UIApplication.shared.keyWindow)
                let c1 = HomeFinanceCoverView().then {
                    $0.step = .s1
                    $0.referPoint = .init(minX: rect.minX, minY: rect.minY, maxX: rect.maxX, maxY: rect.maxY)
                    $0.location.rects = [
                        .init(frame: CGRect(x: rect.minX, y: rect.minY, width: rect.width, height: rect.height), cornerRadius: 6.shtScale)
                    ]
                }
                cover.covers.append(c1)     
       cover.show()
    

    相关文章

      网友评论

          本文标题:iOS 镂空 页面引导

          本文链接:https://www.haomeiwen.com/subject/crmcaltx.html