自定義modal彈出樣式(swift)
阿新 • • 發佈:2019-02-10
0.假設vc是需要彈出的控制器(因為彈窗中有需要處理的事務,所以使用一個控制器管理)
1.彈出選單(使用modal將對應的控制器彈出)
presentViewController(vc, animated: true, completion: nil)
2.如果想要自己決定彈出方式,需要成為其代理
vc.transitioningDelegate = self
vc.modalPresentationStyle = .Custom
3.實現transitioningDelegate的代理方法
//該代理方法用於返回負責轉場的控制器物件
func presentationControllerForPresentedViewController(presented: UIViewController , presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController? {
return PopoverPresentationController(presentedViewController: presented, presentingViewController: presenting)
}
//該代理方法用於告訴系統誰來負責控制器如何彈出
func animationControllerForPresentedController(presented: UIViewController , presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
//當彈窗要彈出的時候就會呼叫該方法
return self
}
//該代理方法用於告訴系統誰來負責控制器如何消失
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
//當彈窗要消失的時候就會呼叫該方法
return self
}
4.自定義顯示和消失時彈窗展現方式
// 該方法用於負責控制器如何彈出和如何消失
// 只要是自定義轉場, 控制器彈出和消失都會呼叫該方法
// 需要在該方法中告訴系統控制器如何彈出和如何消失
// 注意點: 但凡告訴系統我們需要自己來控制控制器的彈出和消失
// 也就是實現了UIViewControllerAnimatedTransitioning的方法之後, 那麼系統就不會再控制我們控制器的動畫了, 所有的操作都需要我們自己完成
// 系統呼叫該方法時會傳遞一個transitionContext引數, 該引數中包含了我們所有需要的值
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
//在這裡自定義出現和消失時的動畫
}
5.自定義蒙版
注意點:
所有被彈出的內容, 都是放在容器檢視上的說明:因為當彈窗出現的時候,背後的控制器是不能處理事件的,所以將繼承自 UIPresentationController的控制器(VC2)的containerView中新增一個UIBottom作為蒙版;
因為UIBottom可以被監聽,在VC2中監聽蒙版,一旦點選,就是要消失彈窗;
繼承自UIPresentationController的控制器負責管理自定義轉場動畫
在VC2中的 containerViewWillLayoutSubviews方法中設定彈窗的尺寸和位置,在 presentationTransitionWillBegin方法中新增蒙版,因為彈出的內容,都是放在容器檢視中的,所以將蒙版新增到containerView中;在該方法中,containerView已經被建立;
6.設定顯示時動畫
因為顯示和消失動畫都是在第4步中的animateTransition方法中設定的,但是怎麼區分是出現還是消失彈窗呢?
在第3步中,
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
//當彈窗要彈出的時候就會呼叫該方法
isPresent = true
return self
}
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
//當彈窗要消失的時候就會呼叫該方法
isPresent = false
return self
}
所以設定一個屬性isPresent,當是彈出時,在animationControllerForPresentedController方法中設定為true;
如果是小數,在animationControllerForDismissedController中設定為false;這樣在第4步中的animateTransition方法中設定動畫則根據屬性的值進行設定;
當isPresent = true 時:(抽取一個方法)
/// 彈出動畫
private func animatePresentedController(transitionContext: UIViewControllerContextTransitioning) {
// 0.獲取動畫時長
let duration = transitionDuration(transitionContext)
// 1.拿到被彈出的控制器的View
guard let toView = transitionContext.viewForKey(UITransitionContextToViewKey) else {
return
}
// 2.將被彈出的控制器View新增到容器檢視上
transitionContext.containerView()?.addSubview(toView)
// 3.執行動畫
// 3.1先將選單的View壓扁
toView.transform = CGAffineTransformMakeScale(1.0, 0.0)
toView.layer.anchorPoint = CGPoint(x: 0.5, y: 0)
// 3.2再清空選單壓扁的形變
UIView.animateWithDuration(duration, animations: { () -> Void in
toView.transform = CGAffineTransformIdentity
}) { (_) -> Void in
// 注意點: 但凡是自定義轉場, 一定要在自定義動畫完成之後告訴系統動畫已經完成了, 否則會出現一些未知異常
transitionContext.completeTransition(true)
}
}
7.設定消失動畫
//動畫消失
private func animateDismissedController(transitionContext: UIViewControllerContextTransitioning) {
// 0.獲取動畫時長
let duration = transitionDuration(transitionContext)
// 1.拿到選單
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)
// 2.執行動畫
UIView.animateWithDuration(duration, animations: { () -> Void in
// 注意; 沒有動畫的原因是MakeScale接收的是CGFloat, 而CGFloat得值是不準確的
fromView?.transform = CGAffineTransformMakeScale(1.0, 0.0001)
}) { (_) -> Void in
transitionContext.completeTransition(true)
}
}