์ž…๋ฌธํ•˜์ง€ ์–ผ๋งˆ ๋˜์ง€ ์•Š์€ ๋‚˜๋Š” ์—ฌ์ „ํžˆ ๊ธฐ๋ณธ Transition๋งŒ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค. ๋‚˜๋„ ํ™”๋ คํ•œ UX๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ณ  ์‹ถ๋‹ค! ๊ทธ ์ „์—, ๊ทธ ์‹œ์ž‘์ด ๋˜๋Š” UIPresentationController๋ถ€ํ„ฐ ์•Œ์•„๋ณด์ž.

์‹œ์ž‘์— ์•ž์„œ

๊ทธ ์ „์—, ์‚ฌ์‹ค Presentation๊ณผ Transition์ด ๋ญ”์ง€๋„ ์ž˜ ๋ชจ๋ฅด๊ฒ ๋‹ค. ์ผ๋‹จ์€ ์œ„์™€ ๊ฐ™์€ ๊ทธ๋ฆผ์„ ๋งˆ์Œ์†์— ์‚ด์ง๋งŒ ๋„ฃ์–ด๋‘์ž. ์ •ํ™•ํ•œ ๊ทธ๋ฆผ์€ ์•„๋‹ˆ๋‹ค. ๋‹ค๋งŒ Presentation์ด๋ผ๋Š” ๊ฐœ๋…์ด Transition์„ ํฌํ•จํ•˜๋Š”, ๊ฐ์‹ธ๋Š” ์ƒ์œ„ ๊ฐœ๋…์ฒ˜๋Ÿผ ๋™์ž‘ํ•œ๋‹ค๋Š” ์ ์„ ๊ธฐ์–ตํ•˜์ž. ๊ทธ๋ฆฌ๊ณ  Animation์€ ์‹ค์ œ animation ๊ด€๋ จ ์ฝ”๋“œ๊ฐ€ ๋“ค์–ด๊ฐ€๋Š” ๋ถ€๋ถ„์ด๋‹ค. ํ•ด๋‹น ๋ถ€๋ถ„์€ ์–ด๋Š ๊ณณ(presentation, transition)์—์„œ๋‚˜ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๋‹ค๋งŒ ์‹ค์ œ ๋™์ž‘์„ ๊ธฐ์ˆ ํ•˜๋Š” ๊ณณ์ด๋ผ ์ƒ๊ฐํ•ด๋‘์ž.

UIPresentationController

์ผ๋‹จ ๊ฐœ๋…๋ถ€ํ„ฐ ์žก๊ณ  ๊ฐ€์•ผํ•œ๋‹ค. ํ™”๋ฉด ์ „ํ™˜์„ ํ•˜๊ณ  ์‹ถ์€๋ฐ, ๋‚˜๋ผ๋ฉด ์–ด๋–ค์‹์œผ๋กœ ๋งŒ๋“ค์–ด์„œ ๊ด€๋ฆฌํ• ๊นŒ? ํ™”๋ฉด์˜ ์ „ํ™˜์— ์žˆ์–ด์„œ ๋ฌด์—‡์ด ํ•„์š”ํ• ์ง€ ํ•œ๋ฒˆ ์ƒ๊ฐํ•ด๋ณด์ž.

์ผ๋‹จ ํ™”๋ฉด ์ „ํ™˜์„ ํ˜ธ์ถœํ•˜๋Š” VC, ๊ทธ๋ฆฌ๊ณ  ์‹ค์ œ๋กœ ๋‹ค์Œ์— ๋ณด์—ฌ์งˆ VC ๋‘ ๋…€์„์ด ํ•„์š”ํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ๋‘ VC๋“ค์ด ์ž ์‹œ๋‚˜๋งˆ Transition์ด ์ผ์–ด๋‚˜๋ฉด์„œ ๋ชธ๋‹ด๊ณ  ์žˆ์„ View๊ฐ™์€ ๊ฒƒ๋„ ํ•„์š”ํ•  ๊ฒƒ์ด๋‹ค. ์‹ค์ œ๋กœ ์ด๋Ÿฌํ•œ ๊ฒƒ๋“ค์„ ๊ด€๋ฆฌํ•ด์ฃผ๋Š” ๋…€์„์ด ๋ฐ”๋กœ UIPresentationController์ด๋‹ค.

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
 
        self.presentingViewController // == self.presentationViewController.presentingViewController
        self.presentedViewController // == self.presentationViewController.presentedViewController
    }
}

viewController instance์— ์ ‘๊ทผํ•˜๋ฉด presentingViewController ์ด์นœ๊ตฌ์™€ presentedViewController ์ด์นœ๊ตฌ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ๋ฐ”๋กœ presentationViewController ์—ฌ๊ธฐ์„œ ์˜ค๋Š” computed property์ด๋‹ค.

์ด๋…€์„์€ ๋ชจ๋“  ViewController๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, ViewController๊ฐ€ ๋ณด์—ฌ์ง„ ์‹œ์ ์—์„œ ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ์ด ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Presentation ๊ด€๋ จ Process๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ViewController์—์„œ ์ž…๋ ฅํ•˜๋Š” modalPresentationStyle, modalTransitionStyle์€ Built-in ๋œ ๊ธฐ๋Šฅ์œผ๋กœ, ๊ฒฐ๊ตญ์—๋Š” UIPresentationViewController๊ฐ€ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๊ฐ„๋‹จํ•œ Transition์˜ ๋ณ€ํ™”๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” ViewController instance์˜ modalPresentationStyle, modalTransitionStyle๋งŒ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๋ณต์žกํ•œ Transition์„ ์ ์šฉํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ์—๋Š” UIModalPresentationStyle.custom์œผ๋กœ modalPresentationStyle๋ฅผ ๋ณ€๊ฒฝํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. ๋ณ€๊ฒฝํ•œ ํ›„์—๋Š” Transition์ด ์ผ์–ด๋‚˜๋Š” Background view์™€ ๊ฐ™์€ ์†์„ฑ๋“ค์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

ํŠน์ • ViewController์—์„œ ์ด๋Ÿฐ ์†์„ฑ์„ ์ ์šฉํ•˜๊ณ  ์‹ถ์œผ๋ฉด, UIViewControllerTransitioningDelegate๋ฅผ ์ฑ„ํƒํ•˜์—ฌ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค. ์–ด๋–ค PresentationViewController๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ธ์ง€, Transition์—์„œ ์‚ฌ์šฉํ•˜๋Š” Animator๋Š” ์–ด๋–ค ๊ฒƒ์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ธ์ง€, interactive animator๋ฅผ ์‚ฌ์šฉํ•  ๊ฑฐ๋ผ๋ฉด ์–ด๋–ค ๊ฒƒ์ธ์ง€๋ฅผ ๋ฌป๊ฒŒ ๋œ๋‹ค. ์—ฌ๊ธฐ์„œ interactive animator๊ฐ€ ๋ญ”์ง€ ๋ชฐ๋ž๋Š”๋ฐ, ํ•ด๋‹น ์˜์ƒ์˜ ํ›„๋ฐ˜๋ถ€๋ฅผ ๋ณด๋ฉด ๋ญ”์ง€ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ์š”์•ฝํ•ด์„œ ๋งํ•˜๋ฉด push๊ฐ€ ์ผ์–ด๋‚˜๋Š”๋ฐ ์ด๊ฑธ ์†๊ฐ€๋ฝ์œผ๋กœ ์™€๋ฆฌ๊ฐ€๋ฆฌ ์น  ์ˆ˜ ์žˆ๋Š” ๊ฑธ ๋งํ•œ๋‹ค. ๊ทธ๊ฒŒ ์•ˆ๋˜๋Š” ๊ฑด ๋ˆ„๋ฅด๋ฉด ๊ทธ๋ƒฅ ์• ๋‹ˆ๋งค์ด์…˜์ด ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

Presentation Context, Frame

์—ฌ๊ธฐ์„œ ์ž ๊น ์ด ๊ฐœ๋…์„ ์•Œ๊ณ ๊ฐ€์ž. ๋ฌธ์„œ๋ฅผ ์ฝ๋‹ค๋ณด๋ฉด Context๋ผ๋Š” ๋‹จ์–ด๊ฐ€ ์ข…์ข… ๋‚˜์˜จ๋‹ค. ์ด๊ฑด Built-In ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค๊ฒŒ ๋˜๋ฉด์„œ ๋ฐœ์ƒํ•œ ๋‹จ์–ด๋กœ ๋ณด์ธ๋‹ค.

๋‚ด๊ฐ€ Apple UIKit ๊ฐœ๋ฐœ์ž๋‹ค. ์–ด๋–ป๊ฒŒ๋“  ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ, ๊ธฐ๋ณธ์ ์œผ๋กœ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” show(navigationController push)์™€ present(modal)์„ ๋งŒ๋“ค์—ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด show๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉ์ž๊ฐ€ ์ž˜๋ชป ์‚ฌ์šฉํ–ˆ๋‹ค. ์ฆ‰, navigationController์—์„œ ๋™์ž‘ํ•˜๋ผ๊ณ  ๋งŒ๋“ค์–ด๋†จ๋”๋‹ˆ, ๋‚˜๊ฐ™์ด ์ดˆ๋ณด์ž๊ฐ€ ๊ทธ๋ƒฅ ์จ๋ฒ„๋ ธ๋‹ค. ์ด๋Ÿด ๊ฒฝ์šฐ์— ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๊ฒŒ ๋งŒ๋“ค๊นŒ?

์ด๋Ÿฌํ•œ ์ƒํ™ฉ์—์„œ Apple ๊ฐœ๋ฐœ์ž๋“ค์€, ํ˜„์žฌ presentation context, ์ฆ‰ ์ƒํ™ฉ์— ๋งž์ถฐ์„œ ๋™์ž‘ํ•˜๋Š” ๊ฐœ๋…์„ ๋งŒ๋“ค์—ˆ๋‹ค. ๋งŒ์•ฝ ํ˜„์žฌ show๊ฐ€ ์‹คํ–‰๋œ ViewController๊ฐ€ NavigationController๋ฅผ Container Controller๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด, push ๋™์ž‘์„ ํ•  ๊ฒƒ์ด๋‹ค. ๋‹จ์ผ ViewController์˜€๋‹ค๋ฉด present ๋™์ž‘์„ ํ•  ๊ฒƒ์ด๋‹ค. ์—ฌ๊ธฐ์„œ ํ˜„์žฌ ๋ฌธ๋งฅ์„ ๋งํ•˜๋Š” ๊ฒƒ์„ presentation context๋ผ ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  presentation context๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ˜„์žฌ presentating controller(๋ณด์—ฌ์ฃผ๋ ค๊ณ  ํ•˜๋Š” VC)๋ฅผ Frame์ด๋ผ ํ•œ๋‹ค.

containerView

Transition์ด ์ผ์–ด๋‚˜๋ฉด์„œ ViewController๊ฐ€ ์†ํ•ด์žˆ๋Š” ๊ณณ์ด๋‹ค. UIKit์€ transitioning delegate๋ฅผ ์ œ๊ณต๋ฐ›์€ ๋’ค์— ํ•ด๋‹น property๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ presented viewController์˜ view์˜ ์กฐ์ƒ์ด๋‹ค. ๋˜ํ•œ transition animation์ด ์ง„ํ–‰๋  ๋•Œ๋Š” presenting view๊นŒ์ง€ container view์— ์ถ”๊ฐ€ํ•œ๋‹ค.

animator๊นŒ์ง€ custom์œผ๋กœ ๋งŒ๋“ค์–ด์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ์— ์ด animator ๊ฐ์ฒด๋“ค์€ container view๋ฅผ containerView property๋กœ ์ ‘๊ทผํ•ด์„œ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํ•œ๋‹ค.

์ •๋ฆฌ

์ด์ฏค ์ฝ์œผ๋ฉด ์™œ Presentation์ด ๊ฐ์‹ธ๋Š” ๊ฐœ๋…์ด๋ผ๊ณ  ํ–ˆ๋Š”์ง€ ์ดํ•ด๊ฐ€ ๋ ์ง€๋„ ๋ชจ๋ฅด๊ฒ ๋‹ค. ์ผ๋‹จ ๋‚˜๋Š” ๊ทธ๋ ‡๊ฒŒ ์ƒ๊ฐํ•˜๊ณ  ์žˆ๋‹ค. ์‹ค์ œ๋กœ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ๊ฐ™์€ UIViewControllerTransitioningDelegate์—์„œ ์ œ๊ณตํ•˜๊ณ  ์žˆ๊ธด ํ•˜์ง€๋งŒ, ์ด๊ฒƒ์ด ๋ณด๋‹ค ๊ฐœ๋…์ ์œผ๋กœ ์ดํ•ด๊ฐ€ ์‰ฌ์› ๋‹ค.

transition animator๋Š” ์ œ๊ณตํ•œ๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ, ์ด ๋•Œ, present๋  ๋•Œ์˜ animator์™€ dismissal๋  ๋•Œ animator ๋‘๊ฐœ๋ฅผ ์ œ๊ณตํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

Presentation Process

์ด์ œ ์–ด๋–ค ์ˆœ์„œ๋กœ Presentation์ด ์ผ์–ด๋‚˜๋Š”์ง€ ์•Œ์•„๋ณด์ž.

  1. Presentation
    • Transition Animation์˜ ์—ฐ์†ํ•œ ๊ฒƒ๋“ค์ด ์Šคํฌ๋ฆฐ์— ํ‘œํ˜„๋˜์–ด ์ƒˆ VC๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ณผ์ •์„ ๋งํ•œ๋‹ค.
  2. Management
    • ํ™”๋ฉด ํšŒ์ „ ๋“ฑ์— ๊ด€๋ จ๋˜์–ด Animation์ด ๋˜์–ด์•ผ ํ•˜๋Š” ๊ฒƒ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.
  3. Dismissal
    • Transition Animation์˜ ์—ฐ์†ํ•œ ๊ฒƒ๋“ค์ด ์Šคํฌ๋ฆฐ์— ํ‘œํ˜„๋˜์–ด ์ด์ „ VC๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ณผ์ •์„ ๋งํ•œ๋‹ค.

์ฐจ๋ก€๋Œ€๋กœ ๊ฐ๊ฐ ์•Œ์•„๋ณด์ž.

Presentation

  1. ViewController๊ฐ€ ์ƒˆ๋กœ์šด ํ™”๋ฉด์„ ์š”์ฒญํ•œ๋‹ค.
  2. TransitioningDelegate์—๊ฒŒ ์–ด๋–ค PresentationController๋ฅผ ์‚ฌ์šฉํ•  ๊ฑด์ง€ ๋ฌผ์–ด๋ณธ๋‹ค. ()
  3. Delegate๊ฐ€ ์ •์˜๋œ PresentationController๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋„˜๊ธด๋‹ค.
  4. PresentationController๋Š” Custom Presentation์„ ์‹คํ–‰ํ•œ๋‹ค.
  5. presentationTransitionWillBegin() ๋ฉ”์†Œ๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค.
    • Transition์—์„œ ์‚ฌ์šฉ๋˜๋Š” CustomView๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
    • Animation์— ํ•„์š”ํ•œ ๋‹ค๋ฅธ ์†์„ฑ๋“ค์„ ์„ค์ •ํ•œ๋‹ค.
  6. Transition์ด ์‹ค์ œ๋กœ ์ง„ํ–‰๋˜๋ฉด์„œ ํ™”๋ฉด์ด ์ „ํ™˜๋œ๋‹ค.
    • ์‹คํ–‰๋˜๋Š” ๋™์•ˆ containerViewWillLayoutSubviews(), containerViewDidLayoutSubviews()๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.
    • ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ overrideํ•˜์—ฌ presentation controller์—์„œ ์‚ฌ์šฉํ•˜๋Š” custom view๋ฅผ updateํ•  ์ˆ˜ ์žˆ๋‹ค.
  7. Transition์ด ์™„๋ฃŒ๋˜๋ฉด presentationTransitionDidEnd() ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

์‹ค์ œ๋กœ ๊ทธ๋Ÿฌํ•œ์ง€ ์˜๋ฌธ์Šค๋Ÿฌ์›Œ ํ…Œ์ŠคํŠธ ํ–ˆ๋‹ค.

presentationTransitionWillBegin Called!!!
containerViewWillLayoutSubviews Called!!!
containerViewDidLayoutSubviews Called!!!
presentationTransitionDidEnd Called!!!

์‹ค์ œ๋กœ ๊ทธ๋žฌ๋‹ค!

Management

๋‹ค์Œ์œผ๋กœ๋Š” Management์ด๋‹ค. ํ™”๋ฉด ํšŒ์ „๋“ฑ์„ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ, size์— ๋ณ€ํ™”๊ฐ€ ์ผ์–ด๋‚œ ๊ฒฝ์šฐ viewWillTransition(to:with:)๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ์ด๊ฒŒ ์ด ์นœ๊ตฌ์™€ ๊ด€๋ จ๋œ์ง€ ์ฒ˜์Œ ์•Œ์•˜๋‹ค. autoLayout์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ํฌ๊ฒŒ ํ•ด์ค„ ์ž‘์—…์€ ์—†๋‹ค.

Dismissal

  1. Presented View Controller๋ฅผ ๋‹ซ์Œ์„ ์š”์ฒญํ•œ๋‹ค.
  2. dismissalTransitionWillBegin() ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.
  3. Dismissal์ด ์ง„ํ–‰๋œ๋‹ค.
  4. Dismissal transition์ด ๋๋‚˜๋ฉด dismissalTransitionDidEnd()์ด ํ˜ธ์ถœ๋œ๋‹ค.

Project

์ด ๊ฐœ๋…์„ ์–ด๋–ป๊ฒŒ ์ฝ”๋“œ๋กœ ํ‘œํ˜„ํ• ์ง€ ํ•˜๋‹ค๊ฐ€ ์ข‹์€ Repo๋ฅผ ๋ฐœ๊ฒฌํ–ˆ๋‹ค. App store๊ฐ€ ์ƒ๋‹นํžˆ ๊น”๋”ํ•œ transition์„ ์ œ๊ณตํ–ˆ์—ˆ๋Š”๋ฐ, ์ด๋ฅผ Cloneํ•œ ์‚ฌ๋žŒ์ด ์žˆ๋”๋ผ.. ๋•ก์‚ผ

๋ชจ๋“  ํ™”๋ฉด์„ ๋ณผ ๊ฑด ์•„๋‹ˆ๊ณ , ๋”ฑ ์ด Transition customํ•œ ๋ถ€๋ถ„๋งŒ ๋ณด๋ ค๊ณ  ํ•œ๋‹ค. ๋ฐ”๋กœ ์ด๋ถ€๋ถ„.

Transition์€ CardDetailViewController โ†’ TodayViewController๋กœ ๋ฐœ์ƒํ•  ๊ฑฐ๋‹ค. ์‚ฌ์‹ค์ƒ Presenting ViewController์—์„œ ๋งŽ์€ ์ž‘์—…์„ ํ•  ๊ฑฐ๋‹ค. ์ฆ‰, CardDetailViewController์—์„œ PresentationViewController๋ฅผ ๋„ฃ์–ด์ค„ ๊ฑฐ๋‹ค. ํ•˜๋‚˜์”ฉ ๋ณด์ž.

CardDetailViewController

์•„, ๋จผ์ € Presenting View Controller, ์ฆ‰ ์—ฌ๊ธฐ์„œ๋Š” Card์˜ Detail์„ ๋ณด์—ฌ์ฃผ๋Š” ์ด ์นœ๊ตฌ๋กœ ๋ถ€ํ„ฐ transition์ด ์‹œ์ž‘๋˜๊ธฐ ๋•Œ๋ฌธ์— CardDetailViewController ์–˜๊ฐ€ Presenting View Controller์ด๋‹ค. ์—ฌ๊ธฐ์„œ moodalPresentationStyle = .custom์œผ๋กœ ๋ฐ”๊ฟ”์ฃผ๊ณ  ์‹œ์ž‘ํ•ด์•ผ ํ•œ๋‹ค!!

class CardDetailViewController: UIViewController {
    init(cell: TodayTableViewCell) {
        self.cell = cell
        super.init(nibName: nil, bundle: nil)
        self.setupTranstion()
    }
    
    private func setupTranstion() {
        modalPresentationStyle = .custom
        transitioningDelegate = self
    }
}

๊ทธ๋ฆฌ๊ณ  ๋‚˜์„œ ์ด์ œ ์ง„์งœ ํ•ต์‹ฌ ๋ถ€๋ถ„์„ ํ™•์ธํ•ด๋ณด์ž.

extension CardDetailViewController: UIViewControllerTransitioningDelegate {
    
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return TodayAnimationTransition(animationType: .present)
    }
    
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return TodayAnimationTransition(animationType: .dismiss)
    }
    
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        return CardPresentationController(presentedViewController: presented, presenting: presenting)
    }
}

ํ•ต์‹ฌ์€ ์ด๋ถ€๋ถ„์ด๋‹ค. ๋ˆŒ๋ €์„ ๋•Œ ์–ด๋–ค PresentationViewController๋ฅผ ์ œ๊ณตํ•  ๊ฒƒ์ธ์ง€, ์–ด๋–ค Animator๋ฅผ ์ œ๊ณตํ•  ๊ฒƒ์ธ์ง€๋ฅผ ์ •์˜ํ•ด์ฃผ๋ฉด ๋œ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” interaction์ด ํ•„์š”์—†์œผ๋ฏ€๋กœ transition animator๋งŒ ์ ์šฉํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. presentํ•  ๋•Œ์™€ dismissํ•  ๋•Œ ๋‘ ๊ฐœ์˜ animator๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

CardPresentationController

๊ทธ๋Ÿฌ๋ฉด ์ด์ œ PresentationViewController๋ฅผ ์–ด๋–ป๊ฒŒ ๋งŒ๋“œ๋Š”์ง€๋งŒ ์•Œ๋ฉด ๋œ๋‹ค.

class CardPresentationController: UIPresentationController {
    
    private lazy var blurView = UIVisualEffectView(effect: nil)
 
}

์ผ๋‹จ ์ด๋…€์„์€ ์ถ”ํ›„ PresentationViewController์˜ containerView์— ๋“ค์–ด๊ฐˆ ๋…€์„์ด๋‹ค. ์‹ค์ œ๋กœ ์•ฑ์Šคํ† ์–ด ์—ด์–ด์„œ ๋ˆ„๋ฅด๋ฉด ๋’ค์— View์— ๋‹ค๋ฅธ View๋กœ ๊ฐ์‹ธ์ง€๋ฉด์„œ ๋ณด์ธ๋‹ค. ๊ทธ๋ž˜์„œ ๋ญ”๊ฐ€ ๊น”๋”ํ•ด๋ณด์ด๋Š” ๊ฑฐ๋‹ค. ๊ทธ Blur๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์นœ๊ตฌ๋‹ค.

class CardPresentationController: UIPresentationController {
 
    override var shouldRemovePresentersView: Bool {
        return false
    }
 
}

๊ทธ ๋‹ค์Œ์œผ๋กœ๋Š” ์ด์นœ๊ตฌ๋‹ค. ์–˜๋Š” ์ด์ œ Animation์ด ๋๋‚œ ํ›„์— ๋’ค์— ๋ฐฐ๊ฒฝ View๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ๋‚ ๋ฆด ๊ฑด์ง€ ๋ฌผ์–ด๋ณด๋Š” ๊ฐ’์ด๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ false๋กœ ๋˜์–ด ์žˆ๋‹ค. ๊ทผ๋ฐ ์™œ ๊ตณ์ด ์ ์—ˆ๋Š”์ง€๋Š” ๋ชจ๋ฅด๊ฒ ๋‹ค. ๋งŒ์•ฝ ๋’ท ๋ฐฐ๊ฒฝ์ด ๊ณ„์†ํ•ด์„œ ํ•„์š”ํ•˜๋‹ค๋ฉด, ์˜ˆ๋ฅผ ๋“ค์–ด VC๊ฐ€ full view๋กœ ์•ˆ๋ณด์ธ๋‹ค๋ฉด ์˜๋ฏธ์žˆ๋Š” ๊ฐ’์ผ ์ˆ˜ ์žˆ๊ฒ ๋‹ค.

class CardPresentationController: UIPresentationController {
    
    override func presentationTransitionWillBegin() {
        let container = containerView!
        blurView.translatesAutoresizingMaskIntoConstraints = false
        container.addSubview(blurView)
        blurView.edges(to: container)
        blurView.alpha = 0.0
        
        presentingViewController.beginAppearanceTransition(false, animated: false)
        presentedViewController.transitionCoordinator!.animate(alongsideTransition: { (ctx) in
            self.blurView.effect = UIBlurEffect(style: .light)
            self.blurView.alpha = 1
        }) { (ctx) in }
    }
    
    override func presentationTransitionDidEnd(_ completed: Bool) {
        presentingViewController.endAppearanceTransition()
    }
    
    override func dismissalTransitionWillBegin() {
        presentingViewController.beginAppearanceTransition(true, animated: true)
        presentedViewController.transitionCoordinator!.animate(alongsideTransition: { (ctx) in
            self.blurView.alpha = 0.0
        }, completion: nil)
    }
    
    override func dismissalTransitionDidEnd(_ completed: Bool) {
        presentingViewController.endAppearanceTransition()
        if completed {
            blurView.removeFromSuperview()
        }
    }
 
}

์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ life cycle๊ณผ ๊ด€๋ จ๋œ ๋…€์„๋“ค์ด ๋‚˜์˜จ๋‹ค. ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด transition์ด ์ผ์–ด๋‚˜๊ธฐ ์ „์— ํ•„์š”ํ•œ ์š”์†Œ๋“ค์„ ์—ฌ๊ธฐ์„œ ์„ธํŒ…ํ•˜๊ณ  ์žˆ์Œ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ €๊ธฐ ๋ณด์ด๋Š” animate(alongsideTransition)์€ animation์„ ๋™์‹œ์— ์‹คํ–‰์‹œํ‚ค๋Š” method์ด๋‹ค.

Reference