เนื่องจากนี่เป็นผลลัพธ์อันดับต้น ๆ ของ Google ฉันจึงคิดว่าฉันจะแบ่งปันสิ่งที่ฉันคิดว่าเป็นวิธีที่มีเหตุผลที่สุด สิ่งที่จะใช้ iOS 7+ การเปลี่ยน API ฉันใช้สิ่งนี้กับ iOS 10 ด้วย Swift 3
มันค่อนข้างง่ายที่จะรวมสิ่งนี้เข้ากับวิธีที่นิเมชั่นUINavigationController
ระหว่างตัวควบคุมมุมมองสองตัวถ้าคุณสร้างคลาสย่อยUINavigationController
และคืนอินสแตนซ์ของคลาสที่สอดคล้องกับUIViewControllerAnimatedTransitioning
โปรโตคอล
ตัวอย่างนี่คือUINavigationController
subclass ของฉัน:
class NavigationController: UINavigationController {
init() {
super.init(nibName: nil, bundle: nil)
delegate = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension NavigationController: UINavigationControllerDelegate {
public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return NavigationControllerAnimation(operation: operation)
}
}
คุณจะเห็นว่าฉันตั้งค่าUINavigationControllerDelegate
ตัวเองและในส่วนขยายในคลาสย่อยของฉันฉันใช้วิธีการUINavigationControllerDelegate
ที่ช่วยให้คุณส่งคืนตัวควบคุมภาพเคลื่อนไหวที่กำหนดเอง (เช่นNavigationControllerAnimation
) คอนโทรลเลอร์ภาพเคลื่อนไหวที่กำหนดเองนี้จะแทนที่ภาพเคลื่อนไหวหุ้นสำหรับคุณ
คุณอาจสงสัยว่าทำไมฉันผ่านการดำเนินการกับNavigationControllerAnimation
อินสแตนซ์ผ่าน initializer ฉันทำสิ่งนี้เพื่อที่ว่าในNavigationControllerAnimation
การนำUIViewControllerAnimatedTransitioning
โพรโทคอลมาใช้ฉันรู้ว่าการดำเนินการคืออะไร (เช่น 'push' หรือ 'pop') สิ่งนี้จะช่วยให้รู้ว่าฉันควรทำอนิเมชั่นประเภทใด ส่วนใหญ่คุณต้องการแสดงภาพเคลื่อนไหวที่แตกต่างกันขึ้นอยู่กับการใช้งาน
ที่เหลือก็ค่อนข้างมาตรฐาน ใช้ฟังก์ชั่นที่จำเป็นสองอย่างในUIViewControllerAnimatedTransitioning
โปรโตคอลและสร้างภาพเคลื่อนไหวตามที่คุณต้องการ:
class NavigationControllerAnimation: NSObject, UIViewControllerAnimatedTransitioning {
let operation: UINavigationControllerOperation
init(operation: UINavigationControllerOperation) {
self.operation = operation
super.init()
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
let containerView = transitionContext.containerView
if operation == .push {
// do your animation for push
} else if operation == .pop {
// do your animation for pop
}
}
}
สิ่งสำคัญคือต้องจำไว้ว่าสำหรับการดำเนินการแต่ละประเภทที่แตกต่างกัน (เช่น 'พุช' หรือ 'ป๊อป) ตัวควบคุมถึงและจากมุมมองจะแตกต่างกัน เมื่อคุณอยู่ในการดำเนินการพุชตัวควบคุมการดูจะเป็นสิ่งที่ถูกผลัก เมื่อคุณอยู่ในการดำเนินการ pop ตัวควบคุมการดูจะเป็นสิ่งที่กำลังถูกเปลี่ยนเป็นและตัวควบคุมจากมุมมองจะเป็นสิ่งที่ถูกเปิดขึ้นมา
นอกจากนี้to
ต้องเพิ่มตัวควบคุมมุมมองเป็นมุมมองย่อยของcontainerView
ในบริบทการเปลี่ยนแปลง
transitionContext.completeTransition(true)
เมื่อเสร็จสิ้นการเคลื่อนไหวของคุณคุณต้องเรียก ถ้าคุณกำลังทำการเปลี่ยนแปลงการโต้ตอบและคุณจะต้องแบบไดนามิกกลับBool
ไปcompleteTransition(didComplete: Bool)
ขึ้นอยู่กับว่าการเปลี่ยนแปลงเสร็จสมบูรณ์ในตอนท้ายของภาพเคลื่อนไหว
ในที่สุด ( การอ่านที่เป็นตัวเลือก ) คุณอาจต้องการดูว่าฉันได้ทำการเปลี่ยนแปลงที่ฉันกำลังทำอย่างไร รหัสนี้ค่อนข้างแฮ็กมากขึ้นและฉันเขียนมันค่อนข้างเร็วดังนั้นฉันจะไม่บอกว่าเป็นรหัสภาพเคลื่อนไหวที่ยอดเยี่ยม แต่มันยังคงแสดงวิธีการทำส่วนของภาพเคลื่อนไหว
ของฉันเป็นการเปลี่ยนแปลงที่ง่ายมาก ๆ ฉันต้องการเลียนแบบอนิเมชั่นแบบเดียวกับที่ UINavigationController ทำแทน แต่แทนที่จะเป็นอนิเมชัน 'หน้าถัดไปที่อยู่เหนือสุด' ฉันต้องการที่จะนำแอนิเมชั่น 1: 1 ของตัวควบคุมมุมมองเก่าออกไปในเวลาเดียวกันกับมุมมองใหม่ ปรากฏขึ้น นี่เป็นผลของการทำให้ตัวควบคุมมุมมองทั้งสองดูเหมือนว่าพวกเขาจะถูกตรึงซึ่งกันและกัน
สำหรับการดำเนินการพุชที่ต้องตั้งค่าtoViewController
ต้นกำเนิดของมุมมองบนหน้าจอปิดแกน x ให้เพิ่มเป็นมุมมองย่อยของcontainerView
ภาพเคลื่อนไหวบนหน้าจอโดยตั้งค่าorigin.x
เป็นศูนย์ ในขณะเดียวกันฉันทำให้fromViewController
มุมมองของผู้ใช้เคลื่อนไหวโดยการorigin.x
ปิดหน้าจอ:
toViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.size.width, dy: 0.0)
containerView.addSubview(toViewController.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext),
delay: 0,
options: [ UIViewAnimationOptions.curveEaseOut ],
animations: {
toViewController.view.frame = containerView.bounds
fromViewController.view.frame = containerView.bounds.offsetBy(dx: -containerView.frame.size.width, dy: 0)
},
completion: { (finished) in
transitionContext.completeTransition(true)
})
การดำเนินการของป๊อปนั้นเป็นสิ่งที่ตรงกันข้าม เพิ่มมุมtoViewController
มองย่อยของcontainerView
และสร้างภาพเคลื่อนไหวไปfromViewController
ทางขวาเมื่อคุณเคลื่อนไหวtoViewController
จากด้านซ้าย:
containerView.addSubview(toViewController.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext),
delay: 0,
options: [ UIViewAnimationOptions.curveEaseOut ],
animations: {
fromViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.width, dy: 0)
toViewController.view.frame = containerView.bounds
},
completion: { (finished) in
transitionContext.completeTransition(true)
})
นี่คือส่วนสำคัญของไฟล์ swift ทั้งหมด:
https://gist.github.com/alanzeino/603293f9da5cd0b7f6b60dc20bc766be