Slide 1

Slide 1 text

৮Γ৺஍ͷྑ͍ Interactive TransitionΛϚελʔ͠Α͏ גࣜձࣾΤ΢ϨΧ ୮ɹढ़وʢShunki Tanʣ iOSDC 2017

Slide 2

Slide 2 text

୮ɹढ़وʢShunki Tanʣ Lead Engineer iOS͕ಘҙ - 3೥൒ Scrum Master PairsͰ6ϲ݄ Framework UIKit @shunkit @shunkitan

Slide 3

Slide 3 text

๻͕ࢥ͏৮Γ৺஍ͷྑ͍ΞϓϦ

Slide 4

Slide 4 text

೔ຊͷΞϓϦͰ Ξχϝʔγϣϯʹͩ͜ΘͬͯΔͳͱ ײ৺ͨ͠ΞϓϦ͸΄ͱΜͲͳ͍

Slide 5

Slide 5 text

ͳͷͰɺօ͞Μ ւ֎ͷΞϓϦʹෛ͚ͳ͍͘Β͍ ৮Γ৺஍ͷྑ͍ΞϓϦΛ࡞͍͖ͬͯ·͠ΐ͏ ๻΋໛ࡧதͰ͢

Slide 6

Slide 6 text

ࠓ೔࿩͢Ξχϝʔγϣϯ

Slide 7

Slide 7 text

Ξχϝʔγϣϯͷجૅ 2෼ by Material Design

Slide 8

Slide 8 text

Easing Curve ease-in-out alpha, scale, ը໘಺ͷҠಈͳͲ

Slide 9

Slide 9 text

Easing Curve ease-out ը໘಺΁ͷҠಈ (move in)

Slide 10

Slide 10 text

Easing Curve ease-in ը໘֎΁ͷҠಈ (move out)

Slide 11

Slide 11 text

Duration 195msʙ ϑϧεΫϦʔϯͷҠಈ ը໘֎΁ͷҠಈ (move out) 225msʙ ը໘಺΁ͷҠಈ (move in) 375msʙ ஗͍ͱײ͡ͳ͍ݶք ʙ400ms

Slide 12

Slide 12 text

Transitionͷجૅ 3෼

Slide 13

Slide 13 text

AnimatedTransitioning InteractiveTransitioning ̎ͭͷϓϩτίϧ

Slide 14

Slide 14 text

AnimatedTransitioning InteractiveTransitioning ̎ͭͷϓϩτίϧ

Slide 15

Slide 15 text

let controller = ImagePreviewViewController() // ը૾ϓϨϏϡʔը໘ͷViewController controller.transitioningDelegate = ImagePreviewTransition() present(controller, animated: true, completion: nil)

Slide 16

Slide 16 text

let controller = ImagePreviewViewController() // ը૾ϓϨϏϡʔը໘ͷViewController controller.transitioningDelegate = ImagePreviewTransition() present(controller, animated: true, completion: nil) TransitionDelegate Transitionͷͱ͖ʹͲΜͳભҠΞχϝʔγϣϯΛߦ͏͔Λܾఆ͢Δ

Slide 17

Slide 17 text

public protocol UIViewControllerTransitioningDelegate : NSObjectProtocol { @available(iOS 2.0, *) optional public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? @available(iOS 2.0, *) optional public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? optional public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? optional public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? @available(iOS 8.0, *) optional public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? }

Slide 18

Slide 18 text

public protocol UIViewControllerTransitioningDelegate : NSObjectProtocol { @available(iOS 2.0, *) optional public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? @available(iOS 2.0, *) optional public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? optional public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? optional public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? @available(iOS 8.0, *) optional public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? }

Slide 19

Slide 19 text

public protocol UIViewControllerTransitioningDelegate : NSObjectProtocol { @available(iOS 2.0, *) optional public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? @available(iOS 2.0, *) optional public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? optional public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? optional public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? @available(iOS 8.0, *) optional public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? }

Slide 20

Slide 20 text

public protocol UIViewControllerTransitioningDelegate : NSObjectProtocol { @available(iOS 2.0, *) optional public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? @available(iOS 2.0, *) optional public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? optional public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? optional public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? @available(iOS 8.0, *) optional public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? }

Slide 21

Slide 21 text

public protocol UIViewControllerTransitioningDelegate : NSObjectProtocol { @available(iOS 2.0, *) optional public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? @available(iOS 2.0, *) optional public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? optional public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? optional public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? @available(iOS 8.0, *) optional public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? }

Slide 22

Slide 22 text

AnimatedTransitioning InteractiveTransitioning ̎ͭͷϓϩτίϧ

Slide 23

Slide 23 text

public protocol UIViewControllerTransitioningDelegate : NSObjectProtocol { @available(iOS 2.0, *) optional public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? @available(iOS 2.0, *) optional public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? optional public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? optional public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? @available(iOS 8.0, *) optional public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? }

Slide 24

Slide 24 text

public protocol UIViewControllerTransitioningDelegate : NSObjectProtocol { @available(iOS 2.0, *) optional public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? @available(iOS 2.0, *) optional public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? optional public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? optional public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? @available(iOS 8.0, *) optional public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? }

Slide 25

Slide 25 text

public protocol UIViewControllerTransitioningDelegate : NSObjectProtocol { @available(iOS 2.0, *) optional public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? @available(iOS 2.0, *) optional public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? optional public func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? optional public func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? @available(iOS 8.0, *) optional public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? }

Slide 26

Slide 26 text

AnimatedTransitioning InteractiveTransitioning ̎ͭͷϓϩτίϧ

Slide 27

Slide 27 text

·ͱΊΔͱ Present Dismiss ௨ৗͷAnimation Interactive Animation AnimatedTransitioning InteractiveTransitioning ্هͷ̐ͭͷΞχϝʔγϣϯΛܾΊΔ TransitioningDelegate

Slide 28

Slide 28 text

Interactive Transitionͷ࣮૷ํ๏ 7෼

Slide 29

Slide 29 text

public protocol UIViewControllerInteractiveTransitioning : NSObjectProtocol { public func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) optional public var completionSpeed: CGFloat { get } optional public var completionCurve: UIViewAnimationCurve { get } /// In 10.0, if an object conforming to UIViewControllerAnimatedTransitioning is /// known to be interruptible, it is possible to start it as if it was not /// interactive and then interrupt the transition and interact with it. In this /// case, implement this method and return NO. If an interactor does not /// implement this method, YES is assumed. @available(iOS 10.0, *) optional public var wantsInteractiveStart: Bool { get } }

Slide 30

Slide 30 text

public protocol UIViewControllerInteractiveTransitioning : NSObjectProtocol { public func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) optional public var completionSpeed: CGFloat { get } optional public var completionCurve: UIViewAnimationCurve { get } /// In 10.0, if an object conforming to UIViewControllerAnimatedTransitioning is /// known to be interruptible, it is possible to start it as if it was not /// interactive and then interrupt the transition and interact with it. In this /// case, implement this method and return NO. If an interactor does not /// implement this method, YES is assumed. @available(iOS 10.0, *) optional public var wantsInteractiveStart: Bool { get } }

Slide 31

Slide 31 text

public protocol UIViewControllerInteractiveTransitioning : NSObjectProtocol { public func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) optional public var completionSpeed: CGFloat { get } optional public var completionCurve: UIViewAnimationCurve { get } /// In 10.0, if an object conforming to UIViewControllerAnimatedTransitioning is /// known to be interruptible, it is possible to start it as if it was not /// interactive and then interrupt the transition and interact with it. In this /// case, implement this method and return NO. If an interactor does not /// implement this method, YES is assumed. @available(iOS 10.0, *) optional public var wantsInteractiveStart: Bool { get } }

Slide 32

Slide 32 text

InteractiveTransitioningͰάάΔͱ…

Slide 33

Slide 33 text

open class UIPercentDrivenInteractiveTransition : NSObject, UIViewControllerInteractiveTransitioning InteractiveTransitioningΛ࠾༻ͨ͠UIKitͷΫϥε

Slide 34

Slide 34 text

open class UIPercentDrivenInteractiveTransition : NSObject, UIViewControllerInteractiveTransitioning ͨͩ͠ɺAnimatedTransitioningͷΞχϝʔγϣϯΛͦͷ··Interactiveʹ͢Δͷ ͰɺΧελϚΠζ͍ͨ͠৔߹ʹ޲͔ͳ͍ InteractiveTransitioningΛ࠾༻ͨ͠UIKitͷΫϥε

Slide 35

Slide 35 text

open class UIPercentDrivenInteractiveTransition : NSObject, UIViewControllerInteractiveTransitioning ͨͩ͠ɺAnimatedTransitioningͷΞχϝʔγϣϯΛͦͷ··Interactiveʹ͢Δͷ ͰɺΧελϚΠζ͍ͨ͠৔߹ʹ޲͔ͳ͍ InteractiveTransitioningΛ࠾༻ͨ͠UIKitͷΫϥε

Slide 36

Slide 36 text

PanGestureͰDismiss͢Δ

Slide 37

Slide 37 text

UIPanGeustureRecognizer ΛࣸਅͷImageViewʹ௥Ճ͢Δ

Slide 38

Slide 38 text

Gesture: .began func handleGesture(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: gesture.view) let velocity = gesture.velocity(in: gesture.view) var progress = abs(translation.y) / 200 progress = CGFloat(min(max(progress, 0.0), 1.0)) switch gesture.state { case .possible: break case .began: interactionInProgress = true previewViewController.dismiss(animated: true, completion: nil) case .changed: updateInteraction(progress: progress, translation: translation) case .cancelled, .failed: interactionInProgress = false completeInteraction(velocity: velocity, finished: false) case .ended: interactionInProgress = false let finished = progress > 0.3 || (progress > 0.1 && abs(velocity.y) > 100) completeInteraction(velocity: velocity, finished: finished) } }

Slide 39

Slide 39 text

Gesture: .began func handleGesture(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: gesture.view) let velocity = gesture.velocity(in: gesture.view) var progress = abs(translation.y) / 200 progress = CGFloat(min(max(progress, 0.0), 1.0)) switch gesture.state { case .possible: break case .began: interactionInProgress = true previewViewController.dismiss(animated: true, completion: nil) case .changed: updateInteraction(progress: progress, translation: translation) case .cancelled, .failed: interactionInProgress = false completeInteraction(velocity: velocity, finished: false) case .ended: interactionInProgress = false let finished = progress > 0.3 || (progress > 0.1 && abs(velocity.y) > 100) completeInteraction(velocity: velocity, finished: finished) } } Dismiss ViewController

Slide 40

Slide 40 text

Gesture: .began func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { isPresenting = false return interactionInProgress ? self : nil } Dismiss ViewController TransitionDelegate nilҎ֎Λฦ͢ͱɺInteractiveTransition͕࢝·Δ

Slide 41

Slide 41 text

Gesture: .began Dismiss ViewController TransitionDelegate InteractiveTransitioning Transitionʹඋ͑ͯɺViewͷ४උͳͲΛ͢Δ func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning)

Slide 42

Slide 42 text

func handleGesture(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: gesture.view) let velocity = gesture.velocity(in: gesture.view) var progress = abs(translation.y) / 200 progress = CGFloat(min(max(progress, 0.0), 1.0)) switch gesture.state { case .possible: break case .began: interactionInProgress = true destinationViewController.dismiss(animated: true, completion: nil) case .changed: updateInteraction(progress: progress, translation: translation) case .cancelled, .failed: interactionInProgress = false completeInteraction(velocity: velocity, finished: false) case .ended: interactionInProgress = false let finished = progress > 0.3 || (progress > 0.1 && abs(velocity.y) > 100) completeInteraction(velocity: velocity, finished: finished) } } Gesture: .began Dismiss ViewController TransitionDelegate InteractiveTransitioning Gesture: .changed

Slide 43

Slide 43 text

UIView.animate(withDuration: 0.1, delay: 0, options: [.allowUserInteraction, .beginFromCurrentState, .curveLinear], animations: { dimmingView.alpha = 1.0 - (0.7 * progress) var center = destinationImageFrame.center center.x += translation.x center.y += translation.y snapshotView.center = center let scale = 1.0 - (0.2 * progress) snapshotView.transform = CGAffineTransform(scaleX: scale, y: scale) }, completion: nil) Duration = 0.1ʹ͢Δͱ଎͘δΣενϟʔΛ͠ ͨͱ͖ʹࣗવʹͳΔ Gesture: .began Dismiss ViewController TransitionDelegate InteractiveTransitioning Gesture: .changed

Slide 44

Slide 44 text

UIView.animate(withDuration: 0.1, delay: 0, options: [.allowUserInteraction, .beginFromCurrentState, .curveLinear], animations: { dimmingView.alpha = 1.0 - (0.7 * progress) var center = destinationImageFrame.center center.x += translation.x center.y += translation.y snapshotView.center = center let scale = 1.0 - (0.2 * progress) snapshotView.transform = CGAffineTransform(scaleX: scale, y: scale) }, completion: nil) Easing Curve͸Linearʹ͢Δ Gesture: .began Dismiss ViewController TransitionDelegate InteractiveTransitioning Gesture: .changed

Slide 45

Slide 45 text

func handleGesture(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: gesture.view) let velocity = gesture.velocity(in: gesture.view) var progress = abs(translation.y) / 200 progress = CGFloat(min(max(progress, 0.0), 1.0)) switch gesture.state { case .possible: break case .began: interactionInProgress = true destinationViewController.dismiss(animated: true, completion: nil) case .changed: updateInteraction(progress: progress, translation: translation) case .cancelled, .failed: interactionInProgress = false completeInteraction(velocity: velocity, finished: false) case .ended: interactionInProgress = false let finished = progress > 0.3 || (progress > 0.1 && abs(velocity.y) > 100) completeInteraction(velocity: velocity, finished: finished) } } Gesture: .ended Gesture: .began Dismiss ViewController TransitionDelegate InteractiveTransitioning Gesture: .changed

Slide 46

Slide 46 text

Gesture: .ended ΞχϝʔγϣϯΛΑΓࣗવʹݟͤΔͨΊʹɺSpringAnimationΛ࢖͍͍ͨ ΞχϝʔγϣϯΛΑΓࣗવʹݟͤΔͨΊʹɺ SpringAnimationΛ࢖͍͍ͨ

Slide 47

Slide 47 text

Gesture: .ended UIView.animate(withDuration duration: TimeInterval, delay: TimeInterval, usingSpringWithDamping dampingRatio: CGFloat, initialSpringVelocity velocity: CGFloat, options: UIViewAnimationOptions = [], animations: @escaping () -> Swift.Void, completion: ((Bool) -> Swift.Void)? = nil )

Slide 48

Slide 48 text

UIView.animate(withDuration duration: TimeInterval, delay: TimeInterval, usingSpringWithDamping dampingRatio: CGFloat, initialSpringVelocity velocity: CGFloat, options: UIViewAnimationOptions = [], animations: @escaping () -> Swift.Void, completion: ((Bool) -> Swift.Void)? = nil ) ݫີʹ΍ΔͳΒɺݮਰৼಈͷӡಈํఔࣜΛղ͘ඞཁ͕͋Δ Gesture: .ended

Slide 49

Slide 49 text

Gesture: .ended ΞχϝʔγϣϯΛΑΓࣗવʹݟͤΔͨΊʹɺSpringAnimationΛ࢖͍͍ͨ

Slide 50

Slide 50 text

func handleGesture(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: gesture.view) let velocity = gesture.velocity(in: gesture.view) var progress = abs(translation.y) / 200 progress = CGFloat(min(max(progress, 0.0), 1.0)) switch gesture.state { case .possible: break case .began: interactionInProgress = true destinationViewController.dismiss(animated: true, completion: nil) case .changed: updateInteraction(progress: progress, translation: translation) case .cancelled, .failed: interactionInProgress = false completeInteraction(velocity: velocity, finished: false) case .ended: interactionInProgress = false let finished = progress > 0.3 || (progress > 0.1 && abs(velocity.y) > 100) completeInteraction(velocity: velocity, finished: finished) } } Gesture: .ended

Slide 51

Slide 51 text

// ΞχϝʔγϣϯΛࣗવʹݟͤΔͨΊʹɺݮਰͷΞχϝʔγϣϯ͕ඞཁ POPDecayAnimation(propertyNamed: kPOPViewCenter).apply { // PanGestureͷvelocity $0.velocity = velocity $0.deceleration = 0.9 $0.completionBlock = { _ in // ݮਰͯ͠WFMPDJUZʹͳͬͨΒɺ // ImageViewͷframe, scaleͷSpringΞχϝʔγϣϯΛ։࢝͢Δ snapshotView.pop_add(frameAnimation, forKey: "snapshot.frame") snapshotView.pop_add(scaleAnimation, forKey: "snapshot.transform") } snapshotView.pop_add($0, forKey: "snapshot.center") } Gesture: .ended 1. ݮਰͷΞχϝʔγϣϯ

Slide 52

Slide 52 text

// ΞχϝʔγϣϯΛࣗવʹݟͤΔͨΊʹɺݮਰͷΞχϝʔγϣϯ͕ඞཁ POPDecayAnimation(propertyNamed: kPOPViewCenter).apply { // PanGestureͷvelocity $0.velocity = velocity $0.deceleration = 0.9 $0.completionBlock = { _ in // ݮਰͯ͠WFMPDJUZʹͳͬͨΒɺ // ImageViewͷframe, scaleͷSpringΞχϝʔγϣϯΛ։࢝͢Δ snapshotView.pop_add(frameAnimation, forKey: "snapshot.frame") snapshotView.pop_add(scaleAnimation, forKey: "snapshot.transform") } snapshotView.pop_add($0, forKey: "snapshot.center") } Gesture: .ended 1. ݮਰͷΞχϝʔγϣϯ

Slide 53

Slide 53 text

// ΞχϝʔγϣϯΛࣗવʹݟͤΔͨΊʹɺݮਰͷΞχϝʔγϣϯ͕ඞཁ POPDecayAnimation(propertyNamed: kPOPViewCenter).apply { // PanGestureͷvelocity $0.velocity = velocity $0.deceleration = 0.9 $0.completionBlock = { _ in // ݮਰͯ͠WFMPDJUZʹͳͬͨΒɺ // ImageViewͷframe, scaleͷSpringΞχϝʔγϣϯΛ։࢝͢Δ snapshotView.pop_add(frameAnimation, forKey: "snapshot.frame") snapshotView.pop_add(scaleAnimation, forKey: "snapshot.transform") } snapshotView.pop_add($0, forKey: "snapshot.center") } Gesture: .ended 1. ݮਰͷΞχϝʔγϣϯ

Slide 54

Slide 54 text

let group = DispatchGroup() POPBasicAnimation.easeInOut(propertyNamed: kPOPViewAlpha).apply { group.enter() $0.duration = duration $0.toValue = finished ? 0.0 : 1.0 $0.completionBlock = { _ in group.leave() } dimmingView.pop_add($0, forKey: "dimming.alpha") } let frameAnimation = POPSpringAnimation(propertyNamed: kPOPViewFrame).apply { group.enter() $0.toValue = finished ? NSValue(cgRect: self.sourceImageFrame!) : NSValue(cgRect: self.destinationImageFrame!) $0.springBounciness = 3 $0.springSpeed = 3 $0.completionBlock = { _ in group.leave() } } let scaleAnimation = POPSpringAnimation(propertyNamed: kPOPViewScaleXY).apply { group.enter() $0.toValue = NSValue(cgSize: CGSize(square: 1)) $0.springBounciness = 3 $0.springSpeed = 3 $0.completionBlock = { _ in group.leave() } } Gesture: .ended 2. SpringΞχϝʔγϣϯ

Slide 55

Slide 55 text

let group = DispatchGroup() POPBasicAnimation.easeInOut(propertyNamed: kPOPViewAlpha).apply { group.enter() $0.duration = duration $0.toValue = finished ? 0.0 : 1.0 $0.completionBlock = { _ in group.leave() } dimmingView.pop_add($0, forKey: "dimming.alpha") } let frameAnimation = POPSpringAnimation(propertyNamed: kPOPViewFrame).apply { group.enter() $0.toValue = finished ? NSValue(cgRect: self.sourceImageFrame!) : NSValue(cgRect: self.destinationImageFrame!) $0.springBounciness = 3 $0.springSpeed = 3 $0.completionBlock = { _ in group.leave() } } let scaleAnimation = POPSpringAnimation(propertyNamed: kPOPViewScaleXY).apply { group.enter() $0.toValue = NSValue(cgSize: CGSize(square: 1)) $0.springBounciness = 3 $0.springSpeed = 3 $0.completionBlock = { _ in group.leave() } } Gesture: .ended 3. શͯͷΞχϝʔγϣϯ͕׬ྃͨ͠Β…

Slide 56

Slide 56 text

group.notify(queue: .main) { if finished { self.previewSource?.sourceImageView?.isHidden = false UIView.animate(withDuration: duration * 0.25, delay: 0, options: [.curveEaseInOut], animations: { snapshotView.alpha = 0 }, completion: { _ in destination.view.removeFromSuperview() dimmingView.removeFromSuperview() snapshotView.removeFromSuperview() transitionContext.finishInteractiveTransition() transitionContext.completeTransition(true) }) } else { // TransitionΛΩϟϯηϧͨ͠ͱ͖ } } Gesture: .ended 3. શͯͷΞχϝʔγϣϯ͕׬ྃͨ͠Β…

Slide 57

Slide 57 text

group.notify(queue: .main) { if finished { self.previewSource?.sourceImageView?.isHidden = false UIView.animate(withDuration: duration * 0.25, delay: 0, options: [.curveEaseInOut], animations: { snapshotView.alpha = 0 }, completion: { _ in destination.view.removeFromSuperview() dimmingView.removeFromSuperview() snapshotView.removeFromSuperview() transitionContext.finishInteractiveTransition() transitionContext.completeTransition(true) }) } else { // TransitionΛΩϟϯηϧͨ͠ͱ͖ } } Gesture: .ended 4.TransitionΛऴྃ͢Δ

Slide 58

Slide 58 text

׬੒ͨ͠Ξχϝʔγϣϯ

Slide 59

Slide 59 text

·ͱΊ ɾEasing CurveɺDurationʹͩ͜ΘΓ·͠ΐ͏ ɾInteractive Transitionͷ࣮૷Λ࣌ܥྻͰઆ໌͠·ͨ͠ ɾΞχϝʔγϣϯΛࣗવʹݟͤΔͨΊʹɺݮਰͱSpringͷΞχϝʔγϣϯΛ ࢖ͬͯΈ·͠ΐ͏

Slide 60

Slide 60 text

Thank you!! @shunkit @shunkitan