Skip to content

Instantly share code, notes, and snippets.

@liuzhida33
Created September 5, 2022 07:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save liuzhida33/876db0b2234cbdc45f4ad68336a684ed to your computer and use it in GitHub Desktop.
Save liuzhida33/876db0b2234cbdc45f4ad68336a684ed to your computer and use it in GitHub Desktop.
适配Uniapp打开方式自定义弹出动画(包裹导航控制器)
/// Modal 转场动画
class PresentTransition: NSObject, UIViewControllerAnimatedTransitioning {
enum Transition {
case present
case dismiss
}
private var tran: Transition = .present
init(_ tran: Transition) {
self.tran = tran
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.35
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
switch tran {
case .present:
presentTransition(transitionContext: transitionContext)
case .dismiss:
dismissTransition(transitionContext: transitionContext)
}
}
private func presentTransition(transitionContext: UIViewControllerContextTransitioning) {
let toVC = transitionContext.viewController(forKey: .to)
guard let toView = toVC?.view else { return }
transitionContext.containerView.addSubview(toView)
toView.transform = CGAffineTransform(translationX: 0, y: UIScreen.main.bounds.height)
UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: .curveEaseIn, animations: {
toView.transform = CGAffineTransform(translationX: 0, y: 0)
}, completion: { _ in
transitionContext.completeTransition(true)
})
}
private func dismissTransition(transitionContext: UIViewControllerContextTransitioning) {
let fromVC = transitionContext.viewController(forKey: .from)
guard let fromView = fromVC?.view else { return }
UIView.animate(withDuration: transitionDuration(using: transitionContext), delay: 0, options: .curveEaseOut, animations: {
fromView.transform = CGAffineTransform.identity.translatedBy(x: 0, y: UIScreen.main.bounds.height)
}, completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}
extension UIViewController {
static func setupSwizzle() {
// 适配Uniapp
swizzleMethod(originalCls: UIViewController.self,
originalSelector: #selector(UIViewController.present(_:animated:completion:)),
swizzledCls: UIViewController.self,
swizzledSelector: #selector(UIViewController.swizzledPresent(_:animated:completion:)))
// 适配Uniapp
swizzleMethod(originalCls: UIViewController.self,
originalSelector: #selector(UIViewController.viewWillAppear(_:)),
swizzledCls: UIViewController.self,
swizzledSelector: #selector(UIViewController.swizzledViewWillAppear(_:)))
}
@objc func swizzledPresent(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
if String(describing: type(of: viewControllerToPresent)) == "DCUniMPViewController" {
// 替换Uniapp弹出容器为NavigationController
let container = UniNavigationController(rootViewController: viewControllerToPresent)
self.swizzledPresent(container, animated: flag, completion: completion)
} else {
self.swizzledPresent(viewControllerToPresent, animated: flag, completion: completion)
}
}
@objc func swizzledViewWillAppear(_ animated: Bool) {
if String(describing: type(of: self)) == "DCUniMPViewController" {
// 设置Uniapp页面隐藏导航
self.navigationController?.setNavigationBarHidden(true, animated: true)
}
self.swizzledViewWillAppear(animated)
}
}
// 更换打开关闭uniapp动画
class UniNavigationController: UINavigationController, UINavigationControllerDelegate {
override init(rootViewController: UIViewController) {
super.init(rootViewController: rootViewController)
// 移出uniapp容器边缘手势
rootViewController.view.gestureRecognizers?.removeAll(where: { $0.isMember(of: UIScreenEdgePanGestureRecognizer.self) })
transitioningDelegate = self
modalPresentationStyle = .custom
}
override func viewDidLoad() {
super.viewDidLoad()
// 设置pop手势代理为自身,解决pop根控制器时出现页面假死
// 在其他页面改变interactivePopGestureRecognizer?.delegate指针指向会使解决方案失效
interactivePopGestureRecognizer?.delegate = self
delegate = self
let popRecognizer = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(handlePopRecognizer(_:)))
popRecognizer.edges = .left
popRecognizer.delegate = self
view.addGestureRecognizer(popRecognizer)
// 设置边缘手势优先级,pop优先级要大于dismiss
if let recognizer = interactivePopGestureRecognizer {
popRecognizer.require(toFail: recognizer)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private let dismissAnimator = UIPercentDrivenInteractiveTransition()
private var isInteractive: Bool = false
@objc func handlePopRecognizer(_ popRecognizer: UIScreenEdgePanGestureRecognizer) {
let progress = abs(popRecognizer.translation(in: UIApplication.shared.keyWindow!).x) / UIApplication.shared.keyWindow!.bounds.width
switch popRecognizer.state {
case .began:
isInteractive = true
dismiss(animated: true)
case .changed:
dismissAnimator.update(progress)
case .ended, .cancelled:
isInteractive = false
if progress > 0.25 {
dismissAnimator.finish()
} else {
dismissAnimator.cancel()
}
case .possible, .failed:
isInteractive = false
dismissAnimator.cancel()
@unknown default:
isInteractive = false
dismissAnimator.cancel()
}
}
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
viewControllers.last?.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
interactivePopGestureRecognizer?.isEnabled = false
super.pushViewController(viewController, animated: animated)
}
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
interactivePopGestureRecognizer?.isEnabled = true
}
}
extension UniNavigationController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
// 当pop到根视图时注意禁用该pop手势,否则出现假死情况
if gestureRecognizer == interactivePopGestureRecognizer, children.count <= 1 {
return false
}
return true
}
}
extension UniNavigationController: UIViewControllerTransitioningDelegate {
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
isInteractive ? dismissAnimator : nil
}
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
PresentTransition(.present)
}
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
PresentTransition(.dismiss)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment