效果圖: 純代碼
1.自定義繼承于UIPresentationController的控制器
import UIKit
class PopoverPresentationController: UIPresentationController{
// 定義一個(gè)presentFrame 來(lái)動(dòng)態(tài)改變展示視圖的frame
var presentFrame = CGRect.zero
/**
實(shí)例化負(fù)責(zé)轉(zhuǎn)場(chǎng)的控制器
:param: presentedViewController 被展現(xiàn)的控制器
:param: presentingViewController 發(fā)起轉(zhuǎn)場(chǎng)的控制器, xocde6是nil, xocde7是野指針
:returns: 負(fù)責(zé)轉(zhuǎn)場(chǎng)的控制器
*/
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
}
/*
即將布局容器視圖上得子視圖時(shí)調(diào)用
containerView 容器視圖, 放置展現(xiàn)出來(lái)的視圖
presentedView 被展現(xiàn)的視圖
*/
override func containerViewWillLayoutSubviews() {
// 1.添加遮蓋
containerView?.addSubview(coverView)
containerView?.insertSubview(coverView, at: 0)
coverView.frame = (containerView?.frame)!
// 把菜單的frame做活
if presentFrame == CGRect.zero {
/*
* 這個(gè)是默認(rèn)菜單高度
*/
// 2.調(diào)整被展示視圖的大小
presentedView?.frame = CGRect(x:JKscreenW/2.0-100,y:56,width:200,height:200)
// 2.1.被展示視圖上面的子視圖大小的調(diào)整
// 背景圖片
presentedView?.subviews[0].frame = CGRect(x:0,y:0,width:200,height:200)
// tableview
presentedView?.subviews[1].frame = CGRect(x:20,y:20,width:presentedView!.width-40,height:presentedView!.height-40)
}else{
/*
* 這個(gè)是外面自定義的菜單高度
*/
// 2.調(diào)整被展示視圖的大小
presentedView?.frame = presentFrame
// 2.1.被展示視圖上面的子視圖大小的調(diào)整
// 背景圖片
presentedView?.subviews[0].frame = CGRect(x:0,y:0,width:presentFrame.width,height:presentFrame.height)
// tableview
presentedView?.subviews[1].frame = CGRect(x:20,y:20,width:presentFrame.width-40,height:presentFrame.height-40)
}
}
/**
關(guān)閉彈窗
*/
func close(){
presentedViewController.dismiss(animated: true, completion: nil)
}
// MARK: - 懶加載
lazy var coverView: UIView = {
// 1.創(chuàng)建蒙版
let view = UIView()
view.backgroundColor = UIColor(white: 0.0, alpha: 0.2)
// 2.注冊(cè)監(jiān)聽(tīng)
let tap = UITapGestureRecognizer(target: self, action: #selector(PopoverPresentationController.close))
view.addGestureRecognizer(tap)
return view
}()
}
2.自定義一個(gè)管理動(dòng)畫(huà)的對(duì)象類PopoverAnimator繼承于NSObject,遵守UIViewControllerTransitioningDelegate,UIViewControllerAnimatedTransitioning協(xié)議
import UIKit
// 定義常量保存通知的名稱
let JKPopoverAnimatorWillshow = "JKPopoverAnimatorWillshow"
let JKPopoverAnimatorWilldismiss = "JKPopoverAnimatorWilldismiss"
/*
* 負(fù)責(zé)轉(zhuǎn)場(chǎng)的類
*/
class PopoverAnimator: NSObject,UIViewControllerTransitioningDelegate,UIViewControllerAnimatedTransitioning{
// 記錄當(dāng)前菜單是否展開(kāi)
var isPresent: Bool = false
var presentFrame = CGRect.zero
// MARK: 只要實(shí)現(xiàn)了一下方法,那么系統(tǒng)自帶的默認(rèn)動(dòng)畫(huà)就沒(méi)有了莱革,所有東西都需要程序員自己來(lái)實(shí)現(xiàn)
// 實(shí)現(xiàn)代理方法峻堰,告訴系統(tǒng)誰(shuí)來(lái)負(fù)責(zé)轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
// UIPopoverPresentationController ios8推出專門負(fù)責(zé)轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController?{
let pc = PopoverPresentationController(presentedViewController: presented, presenting: presenting)
// 傳frame,設(shè)置菜單的大小
pc.presentFrame = presentFrame
return pc
}
// 告訴系統(tǒng)誰(shuí)來(lái)負(fù)責(zé)Modal的展現(xiàn)動(dòng)畫(huà)
// presented : 被展現(xiàn)的視圖
// presenting : 發(fā)起的視圖
// returns : 誰(shuí)來(lái)負(fù)責(zé)
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning?{
NotificationCenter.default.post(name: NSNotification.Name(rawValue: JKPopoverAnimatorWillshow), object: self)
isPresent = true
return self
}
// 告訴系統(tǒng)誰(shuí)來(lái)負(fù)責(zé)modal的消失動(dòng)畫(huà)
// dismissed 被關(guān)閉的視圖
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?{
NotificationCenter.default.post(name: NSNotification.Name(rawValue: JKPopoverAnimatorWilldismiss), object: self)
isPresent = false
return self
}
/*
* UIViewControllerAnimatedTransitioning 下面的2個(gè)方法是這個(gè)協(xié)議產(chǎn)生的
* 返回動(dòng)畫(huà)時(shí)長(zhǎng)
* transitionContext : 上下文里面包含了所有需要的參數(shù)
* returns: 動(dòng)畫(huà)時(shí)長(zhǎng)
*
*/
public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.5
}
/*
* 告訴系統(tǒng)如何動(dòng)畫(huà),無(wú)論是展現(xiàn)還是
*
*
*/
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
// 1.拿到展現(xiàn)的視圖
// let toVc = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
// let fromVc = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
// 通過(guò)打印發(fā)現(xiàn)需要修改的是 toVc上面的view
print(isPresent)
if isPresent {
let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)
// 注意: 一定要把視圖添加到view上
transitionContext.containerView.addSubview(toView!)
// 把 transform設(shè)置為 x:1.0,y: 0
toView?.transform = CGAffineTransform(scaleX: 1.0, y: 0)
// 設(shè)置錨點(diǎn)
toView?.layer.anchorPoint = CGPoint(x:0.5, y: 0)
//print(toView!.layer.anchorPoint)
// 2.執(zhí)行動(dòng)畫(huà)
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
// 2.1.清空transform
toView?.transform = CGAffineTransform.identity
}) { (_) in
// 2.2.動(dòng)畫(huà)執(zhí)行完畢盅视,一定要告訴系統(tǒng),如果不寫可能產(chǎn)生一些未知的錯(cuò)誤
transitionContext.completeTransition(true)
}
}else{
let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)
// 2.執(zhí)行動(dòng)畫(huà)
UIView.animate(withDuration: transitionDuration(using: transitionContext)-3, animations: {
// 把 transform設(shè)置為 x:1.0,y: 0,由于CGFloat是不準(zhǔn)確的捐名,所以寫0.0是沒(méi)有動(dòng)畫(huà)的
fromView?.transform = CGAffineTransform(scaleX: 1.0, y: 0.000001)
}) { (_) in
// 2.2.動(dòng)畫(huà)執(zhí)行完畢,一定要告訴系統(tǒng),如果不寫可能產(chǎn)生一些未知的錯(cuò)誤
transitionContext.completeTransition(true)
}
}
}
}
提醒動(dòng)畫(huà)執(zhí)行完畢闹击,一定要告訴系統(tǒng),如果不寫可能產(chǎn)生一些未知的錯(cuò)誤
transitionContext.completeTransition(true)
3.動(dòng)畫(huà)實(shí)現(xiàn)的操作
// 1.創(chuàng)建控制器對(duì)象
let popoverViewController = PopoverViewController()
// 2.設(shè)置轉(zhuǎn)場(chǎng)代理, 告訴系統(tǒng)誰(shuí)來(lái)負(fù)責(zé)轉(zhuǎn)場(chǎng) popoverAnimator: 是負(fù)責(zé)轉(zhuǎn)場(chǎng)的
popoverViewController.transitioningDelegate = popoverAnimator
// 3.設(shè)置轉(zhuǎn)場(chǎng)模式
popoverViewController.modalPresentationStyle = UIModalPresentationStyle.custom
present(popoverViewController, animated: true, completion: nil)