Saya baru saja membuat tutorial untuk menarik modal secara interaktif untuk mengabaikannya.
http://www.thorntech.com/2016/02/ios-tutorial-close-modal-dragging/
Saya menemukan topik ini membingungkan pada awalnya, jadi tutorial menyusun ini langkah demi langkah.

Jika Anda hanya ingin menjalankan kodenya sendiri, ini adalah reponya:
https://github.com/ThornTechPublic/InteractiveModal
Ini adalah pendekatan yang saya gunakan:
Lihat Controller
Anda mengganti animasi penutupan dengan yang khusus. Jika pengguna menyeret modal, interactortendangan masuk.
import UIKit
class ViewController: UIViewController {
let interactor = Interactor()
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destinationViewController = segue.destinationViewController as? ModalViewController {
destinationViewController.transitioningDelegate = self
destinationViewController.interactor = interactor
}
}
}
extension ViewController: UIViewControllerTransitioningDelegate {
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return DismissAnimator()
}
func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return interactor.hasStarted ? interactor : nil
}
}
Singkirkan Animator
Anda membuat animator khusus. Ini adalah animasi kustom yang Anda paketkan di dalam UIViewControllerAnimatedTransitioningprotokol.
import UIKit
class DismissAnimator : NSObject {
}
extension DismissAnimator : UIViewControllerAnimatedTransitioning {
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.6
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
guard
let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey),
let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey),
let containerView = transitionContext.containerView()
else {
return
}
containerView.insertSubview(toVC.view, belowSubview: fromVC.view)
let screenBounds = UIScreen.mainScreen().bounds
let bottomLeftCorner = CGPoint(x: 0, y: screenBounds.height)
let finalFrame = CGRect(origin: bottomLeftCorner, size: screenBounds.size)
UIView.animateWithDuration(
transitionDuration(transitionContext),
animations: {
fromVC.view.frame = finalFrame
},
completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
}
)
}
}
Interactor
Anda membuat subkelas UIPercentDrivenInteractiveTransitionsehingga dapat bertindak sebagai mesin negara Anda. Karena objek interaktor diakses oleh kedua VC, gunakan itu untuk melacak kemajuan panning.
import UIKit
class Interactor: UIPercentDrivenInteractiveTransition {
var hasStarted = false
var shouldFinish = false
}
Modal View Controller
Ini memetakan status gerakan geser ke panggilan metode interaksionor. The translationInView() ynilai menentukan apakah pengguna melewati ambang batas. Saat isyarat pan adalah .Ended, interaksinya akan menyelesaikan atau membatalkan.
import UIKit
class ModalViewController: UIViewController {
var interactor:Interactor? = nil
@IBAction func close(sender: UIButton) {
dismissViewControllerAnimated(true, completion: nil)
}
@IBAction func handleGesture(sender: UIPanGestureRecognizer) {
let percentThreshold:CGFloat = 0.3
let translation = sender.translationInView(view)
let verticalMovement = translation.y / view.bounds.height
let downwardMovement = fmaxf(Float(verticalMovement), 0.0)
let downwardMovementPercent = fminf(downwardMovement, 1.0)
let progress = CGFloat(downwardMovementPercent)
guard let interactor = interactor else { return }
switch sender.state {
case .Began:
interactor.hasStarted = true
dismissViewControllerAnimated(true, completion: nil)
case .Changed:
interactor.shouldFinish = progress > percentThreshold
interactor.updateInteractiveTransition(progress)
case .Cancelled:
interactor.hasStarted = false
interactor.cancelInteractiveTransition()
case .Ended:
interactor.hasStarted = false
interactor.shouldFinish
? interactor.finishInteractiveTransition()
: interactor.cancelInteractiveTransition()
default:
break
}
}
}