转场动画框架Hero

2020-08-05  本文已影响0人  啧啧同学

这框架真的十分牛逼Hero

分析就不说了,别人文采更好
Hero 分析一Hero 分析二

使用 Hero来做一个弹窗页面,支持手势拖动( 木有会员,放不了视频,效果参考点击 抖音评论的弹窗动画效果及手势拖动效果)

IMG_0909.PNG

手势拖动


IMG_0910.PNG

代码实现

1.首先构建一个弹窗的基类

import UIKit
import Hero
import SnapKit

class FCCommonPopupController: YFBaseViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        navigationBarHidden = true
        view.backgroundColor = .clear
        setupSubviews()
    }
    
    func setupSubviews() {
       
        view.addSubview(bgView)
        view.addSubview(containerView)
        containerView.addSubview(barView)
        
        //主题切换
        themeConfig { (v, t) in
            guard let vc = v as? FCCommonPopupController, let theme = t as? FCThemeColor else { return }
            
            vc.bgView.backgroundColor = theme.cellItemColor.withAlphaComponent(0.3)
            vc.containerView.backgroundColor = theme.dialogColor
            vc.barView.backgroundColor = theme.indicatorColor
        }
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        bgView.snp.makeConstraints { (make) in
            make.edges.equalToSuperview()
        }
        
        containerView.snp.makeConstraints { (make) in
            make.left.right.bottom.equalToSuperview()
            make.height.equalTo(containerHeight)
        }
        
        barView.snp.makeConstraints { (make) in
            make.size.equalTo(CGSize(width: 40, height: 5))
            make.top.equalTo(containerView).offset(10)
            make.centerX.equalTo(containerView)
        }
    }
    
    private func dismiss() {
        navigationController?.hero.isEnabled = true
        dismiss(animated: true, completion: nil)
    }
    
    @objc func tapDismiss(_ tap: UITapGestureRecognizer) {
        dismiss()
    }
    
    @objc func handlePan(gr: UIPanGestureRecognizer) {
        let translation = gr.translation(in: view)
        switch gr.state {
        case .began:
            dismiss()
        case .changed:
            Hero.shared.update(translation.y / (view.bounds.height))
        default:
            let velocity = gr.velocity(in: view)
            if ((translation.y + velocity.y) / view.bounds.height) > 0.5 {
                Hero.shared.finish()
            } else {
                Hero.shared.cancel()
            }
        }
    }

    //MARK: - Property
    //弹窗的高度,子类可覆盖
    var containerHeight: CGFloat {
        return 350.0
    }
    /// 背景视图
    lazy var bgView: UIView = {
        let view = UIView()
        let tap = UITapGestureRecognizer(target: self, action: #selector(tapDismiss(_:)))
        view.addGestureRecognizer(tap)
        
        return view
    }()

    
    /// 视图容器
    lazy var containerView: YFCornerView = {
        let view = YFCornerView(corners: [.topRight, .topLeft], cornerRadius: 12)
        let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(gr:)))
        view.addGestureRecognizer(pan)
        
        let layer = CAGradientLayer()
        layer.frame = CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: containerHeight)
        // 设置渐变色
        let theme = YFThemeManager.current()
        layer.cornerRadius = 12.0
        layer.masksToBounds = true
        layer.colors = [theme.brandColor.withAlphaComponent(0.8).cgColor, theme.dialogColor.withAlphaComponent(0.5).cgColor,
                        theme.dialogColor.cgColor]
        layer.locations = [0.0, 0.9, 1.0]
        view.layer.addSublayer(layer)
        
        return view
    }()
    
    /// 拖动视图
    lazy var barView: UIView = {
        let view = UIView()
        view.layer.cornerRadius = 3
        view.layer.masksToBounds = true
        
        return view
    }()
}

//圆角View
class YFCornerView: UIView {
    
    var corners: UIRectCorner = [.topRight, .topLeft]
    
    var cornerRadius: CGFloat = 0
    
    override var backgroundColor: UIColor? {
        didSet {
            guard self.backgroundColor != .clear else {
                return
            }
            
            tBackgroundColor = self.backgroundColor
            self.backgroundColor = .clear
        }
    }
    
    private var tBackgroundColor: UIColor? = .white

    convenience init(corners: UIRectCorner, cornerRadius: CGFloat) {
        self.init(frame: .zero)
        self.corners = corners
        self.cornerRadius = cornerRadius
    }
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        drawCornerRadius(in: rect)
    }
    
    func drawCornerRadius(in rect: CGRect) {
        let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
        tBackgroundColor?.setFill()
        path.fill()
    }
}

可通过继承FCCommonPopupController来为containerView添加各式各样的界面,🌰:

class FCTestPopupController: FCCommonPopupController {

    override func viewDidLoad() {
        super.viewDidLoad()
       
        setupSubviews()
    }

    //Method
    override func setupSubviews() {
        super.setupSubviews()
        
        containerView.addSubview(testView)
        testView.snp.makeConstraints { (make) in
            make.left.right.bottom.equalToSuperview()
            make.top.equalTo(barView.snp.bottom).offset(5.0)
        }
    }

    // MARK: - Property
    @objc lazy var testView: YourView = {        
        let view = YourView()
         return view
    }()  
}

转场实现

class FCRootViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        .....
    }
     .....

    @objc func onShowToolListBtnClickEvent(_ sender: UIButton) {
          showMenu(self.view)
    }

    /*
       实现起来十分简单:只需要修改对应view 的modifiers参数
       1. 比如背景的转场动画是fade, 则bgView.hero.modifiers = [.fade]
       2.containerView的转场是从下至上,则可以containerView.hero.modifiers = [.translate(y: UIScreen.main.bounds.height)]
    */ 
,

    private func showMenu(_ sourceView: UIView) {
        let vc = FCTestPopupController()
        vc.bgView.hero.modifiers = [.fade]
        vc.containerView.hero.modifiers = [.translate(y: UIScreen.main.bounds.height)]
        
        let nav = YFNavigationController(rootViewController: vc)
        nav.hero.isEnabled = true
        nav.view.backgroundColor = .clear
        nav.modalPresentationStyle = .overFullScreen
        nav.hero.modalAnimationType = .none
        
        present(nav, animated: true, completion: {
            vc.containerView.hero.modifiers?.append(.timingFunction(.linear))
        })
    }
}
上一篇 下一篇

猜你喜欢

热点阅读