Właśnie stworzyłem samouczek do interaktywnego przeciągania w dół modalu, aby go odrzucić.
http://www.thorntech.com/2016/02/ios-tutorial-close-modal-dragging/
Na początku stwierdziłem, że ten temat jest zagmatwany, więc samouczek buduje to krok po kroku.

Jeśli chcesz samemu uruchomić kod, oto repozytorium:
https://github.com/ThornTechPublic/InteractiveModal
Oto podejście, które zastosowałem:
Wyświetl kontroler
Zastępujesz animację zamknięcia niestandardową. Jeśli użytkownik przeciąga modal, interactorzaczyna działać.
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
}
}
Zamknij Animator
Tworzysz niestandardowy animator. To jest niestandardowa animacja umieszczona w UIViewControllerAnimatedTransitioningprotokole.
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
Tworzysz podklasę, UIPercentDrivenInteractiveTransitionaby mogła działać jako twoja maszyna stanu. Ponieważ do obiektu międzyaktorowego mają dostęp oba VC, użyj go do śledzenia postępu panoramowania.
import UIKit
class Interactor: UIPercentDrivenInteractiveTransition {
var hasStarted = false
var shouldFinish = false
}
Kontroler widoku modalnego
Powoduje to odwzorowanie stanu gestu panoramy na wywołania metod interactor. translationInView() yWartość określa, czy użytkownik przekroczył próg. Kiedy gest przesunięcia jest .Ended, interaktor kończy lub anuluje.
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
}
}
}