转场动画框架Hero
2020-08-05 本文已影响0人
啧啧同学
这框架真的十分牛逼Hero
分析就不说了,别人文采更好
Hero 分析一 和 Hero 分析二
使用 Hero来做一个弹窗页面,支持手势拖动( 木有会员,放不了视频,效果参考点击 抖音评论的弹窗动画效果及手势拖动效果)
手势拖动
代码实现
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))
})
}
}