Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

미려한 UI/UX를 위한 여정

110 views

Published on

letswift 2019
CADisplayLink를 이용한 애니메이션 처리기
Sequential execution 지원 라이브러리 작성하기

Published in: Education
  • Be the first to comment

  • Be the first to like this

미려한 UI/UX를 위한 여정

  1. 1. Animation in iOS Looper Code : https://github.com/dotNetTree/letswift2019
  2. 2. Animation in iOS
  3. 3. (0, 0) (40, 0)
  4. 4. (0, 0) (40, 0)
  5. 5. • 1 center (0, 0) -> center (40, 0) (0, 0) (40, 0)
  6. 6. • 1 center (0, 0) -> center (40, 0) (0, 0) (40, 0) vCircle.center = CGPoint.init(x: 0, y: 0) UIView.animate(withDuration: 1) { vCircle.center = CGPoint.init(x: 40, y: 0) }
  7. 7. • 1 center (0, 0) -> center(40, 0) (0, 0) (40, 0) vCircleCenterX.constant = 40 UIView.animate(withDuration: 1) { vCircle.superview?.layoutIfNeeded() }
  8. 8. (100, 100)
  9. 9. (100, 50) (100, 100)
  10. 10. (100, 100) let circlePath = UIBezierPath( arcCenter: CGPoint(x: 100, y: 100), radius: 50, startAngle: 0, endAngle: .pi * 2, clockwise: true ) let animation = CAKeyframeAnimation( keyPath: #keyPath(CALayer.position) ) animation.duration = 1 animation.path = circlePath.cgPath animation.fillMode = .forwards animation.isRemovedOnCompletion = false vCircle.layer.add(animation, forKey: nil) (100, 50)
  11. 11. • 3 center (0, 0) -> center (40, 0) • 1.5 alpha 0.9 -> alpha 0.3 (0, 0) (40, 0)
  12. 12. vCircle.center = CGPoint(x: 0, y: 0) vCircle.alpha = 1 UIView.animate(withDuration: 1.5, animations: { vCircle.center = CGPoint(x: 20, y: 0) vCircle.alpha = 0.3 }, completion: { _ in UIView.animate(withDuration: 1.5) { vCircle.center = CGPoint(x: 40, y: 0) } })
  13. 13. vCircle.center = CGPoint(x: 0, y: 0) vCircle.alpha = 1 UIView.animate(withDuration: 1.5, animations: { vCircle.center = CGPoint(x: 20, y: 0) vCircle.alpha = 0.3 }, completion: { _ in UIView.animate(withDuration: 1.5) { vCircle.center = CGPoint(x: 40, y: 0) } })
  14. 14. vCircle.center = CGPoint(x: 0, y: 0) vCircle.alpha = 1 UIView.animate(withDuration: 3) { vCircle.center = CGPoint(x: 40, y: 0) } UIView.animate(withDuration: 1.5) { vCircle.alpha = 0 }
  15. 15. • (Refresh Rate) • 1 • Hz (60Hz = 1 60 )
  16. 16. • FPS (Frame Per Second) • - • Hz FPS • (Device)
  17. 17. • CADisplayLink
  18. 18. • CADisplayLink • A timer object that allows your application to synchronize its drawing to the refresh rate of the display
  19. 19. • CADisplayLink • A timer object that allows your application to synchronize its drawing to the refresh rate of the display •
  20. 20. vCircle.center = CGPoint(x: 0, y: 0) vCircle.alpha = 1 UIView.animate(withDuration: 3) { vCircle.center = CGPoint(x: 40, y: 0) } UIView.animate(withDuration: 1.5) { vCircle.alpha = 0 }
  21. 21. func aniStart() { displayLink = CADisplayLink(target: self, selector: #selector(update)) displayLink?.add(to: .main, forMode: .common) start1 = now() ; start2 = now() term1 = 3 ; term2 = 1.5 isEnd1 = false; isEnd2 = false } @objc func update() { ani1(); ani2() } func ani1() { guard !isEnd1 else { return } let rate = min(1.0, (now() - start1) / term1) if rate == 1.0 { isEnd1 = true } vCircle.center = CGPoint(x: 40.0 * rate, y: 0) } func ani2() { guard !isEnd2 else { return } let rate = min(1.0, (now() - start2) / term2) if rate == 1.0 { isEnd2 = true } vCircle.alpha = CGFloat(1.0 - rate) }
  22. 22. Looper
  23. 23. func aniStart() { displayLink = CADisplayLink(target: self, selector: #selector(update)) displayLink?.add(to: .main, forMode: .common) start1 = now() ; start2 = now() term1 = 3 ; term2 = 1.5 isEnd1 = false; isEnd2 = false } @objc func update() { ani1(); ani2() } func ani1() { guard !isEnd1 else { return } let rate = min(1.0, (now() - start1) / term1) if rate == 1.0 { isEnd1 = true } vCircle.center = CGPoint(x: 40.0 * rate, y: 0) } func ani2() { guard !isEnd2 else { return } let rate = min(1.0, (now() - start2) / term2) if rate == 1.0 { isEnd2 = true } vCircle.alpha = CGFloat(1.0 - rate) }
  24. 24. func aniStart() { displayLink = CADisplayLink(target: self, selector: #selector(update)) displayLink?.add(to: .main, forMode: .common) start1 = now() ; start2 = now() term1 = 3 ; term2 = 1.5 isEnd1 = false; isEnd2 = false } @objc func update() { ani1(); ani2() } func ani1() { guard !isEnd1 else { return } let rate = min(1.0, (now() - start1) / term1) if rate == 1.0 { isEnd1 = true } vCircle.center = CGPoint(x: 40.0 * rate, y: 0) } func ani2() { guard !isEnd2 else { return } let rate = min(1.0, (now() - start2) / term2) if rate == 1.0 { isEnd2 = true } vCircle.alpha = CGFloat(1.0 - rate) } class Item { typealias Block = (Item) -> Void static let emptyBlock: Block = { _ in } var start = 0.0 var term = 0.0 var rate = 0.0 var current = 0.0 var block: Block = Item.emptyBlock var ended: Block = Item.emptyBlock var next: Item? = nil var isStop = false fileprivate var marked = false … }
  25. 25. func aniStart() { … } @objc func update() { ani1(); ani2() } func ani1() { guard !isEnd1 else { return } let rate = min(1.0, (now() - start1) / term1) if rate == 1.0 { isEnd1 = true } vCircle.center = CGPoint(x: 40.0 * rate, y: 0) } func ani2() { guard !isEnd2 else { return } let rate = min(1.0, (now() - start2) / term2) if rate == 1.0 { isEnd2 = true } vCircle.alpha = CGFloat(1.0 - rate) }
  26. 26. func aniStart() { … } @objc func update() { ani1(); ani2() } func ani1() { guard !isEnd1 else { return } let rate = min(1.0, (now() - start1) / term1) if rate == 1.0 { isEnd1 = true } vCircle.center = CGPoint(x: 40.0 * rate, y: 0) } func ani2() { guard !isEnd2 else { return } let rate = min(1.0, (now() - start2) / term2) if rate == 1.0 { isEnd2 = true } vCircle.alpha = CGFloat(1.0 - rate) } class Looper { private var items = Collection<Item>() private var pool = Collection<Item>() … fileprivate func loop() { let c = now() let _items = items.elements var cnt = _items.count var hasRemoveItems = false while 0 < cnt { cnt -= 1 let item = _items[cnt] … item.rate = { … }() item.block(item) … } if hasRemoveItems { … } } }
  27. 27. looper.invoke { (dsl) in dsl.time = 0.3 dsl.block = { item in vCircle.alpha = CGFloat(item.rate) } }
  28. 28. Looper looper.invoke { (dsl) in dsl.time = 0.3 dsl.block = { item in vCircle.alpha = CGFloat(item.rate) } } @discardableResult func invoke( _ block: (ItemDSL) -> Void ) -> Sequence { let dsl = ItemDSL() block(dsl) let item = getItem(dsl) item.start += now() item.end = item.start + item.term items.append(item) sequence.current = item return sequence }
  29. 29. Updater private var displayLink: CADisplayLink? func aniStart() { displayLink = CADisplayLink(target: self, selector: #selector(update)) displayLink?.add(to: .main, forMode: .common) … } @objc func update() { ... }
  30. 30. Updater class Updater { private var displayLink: CADisplayLink? fileprivate var loopers = [Looper]() func start() { displayLink = CADisplayLink( target: self, selector: #selector(update) ) displayLink?.add(to: .main, forMode: .common) } … @objc func update() { loopers.forEach { $0.loop() } } }
  31. 31. Looper looper.invoke { dsl in dsl.time = 3 dsl.block = { item in self.vCircle.center = CGPoint(x: 40.0 * item.rate, y: 0) } } looper.invoke { dsl in dsl.time = 1.5 dsl.block = { item in self.vCircle.alpha = CGFloat(1.0 - item.rate) } } 10
  32. 32. Demo
  33. 33. Demo Code : https://github.com/dotNetTree/letswift2019
  34. 34. Demo Code : https://github.com/dotNetTree/letswift2019
  35. 35. Looper Invokeitem loop Items Pool item Updater update
  36. 36. Section 1 Section 2 Section 3
  37. 37. Section 1 Section 2 Section 3
  38. 38. Section 1 Section 2 Section 3
  39. 39. Section 1 Section 2 Section 3 API 1 API 2 API 3 fetch 1 fetch 2 fetch 3 0.2sec 0.8sec 0.3sec
  40. 40. Section 1 Section 2Section 3
  41. 41. func fetch1(completion: @escaping (Response) -> Void) { Alamofire.request(“api/fetch1").response { completion($0.response!) } } … fetch1 { response1 in fetch2 { response2 in fetch3 { response3 in section3.render(with: response3) } section2.render(with: response2) } section1.render(with: response1) }
  42. 42. var res1: Response!; var res2: Response!; var res3: Response! func fetchAll() { fetch1 { r in res1 = r; render(); } fetch2 { r in res2 = r; render(); } fetch3 { r in res3 = r; render(); } } func render() { guard let res1 = res1, let res2 = res2, let res3 = res3 else { return } section1.render(with: res1) section2.render(with: res2) section3.render(with: res3) }
  43. 43. var res1: Response!; var res2: Response!; var res3: Response! func fetchAll() { fetch1 { r in res1 = r; render(); } fetch2 { r in res2 = r; render(); } fetch3 { r in res3 = r; render(); } } func render() { guard let res1 = res1, let res2 = res2, let res3 = res3 else { return } section1.render(with: res1) section2.render(with: res2) section3.render(with: res3) }
  44. 44. var res1: Response! looper.invoke { dsl in fetch1 { r in res1 = r } dsl.isInfinity = true dsl.block = { item in if res1 != nil { section1.render(with: res1); item.isStop = true } } }
  45. 45. var res1: Response!; var res2: Response! looper.invoke { dsl in fetch1 { r in res1 = r } dsl.isInfinity = true dsl.block = { item in if res1 != nil { section1.render(with: res1); item.isStop = true } } }.next { dsl in fetch2 { r in res2 = r } dsl.isInfinity = true dsl.block = { item in if res2 != nil { section2.render(with: res2); item.isStop = true } } }
  46. 46. Flow - async Flow() .async { sync in /* start */ } .async { sync in /* start */ } .start()
  47. 47. Flow - async Flow() .async { sync in /* start */ } .async { sync in /* start */ } .start()
  48. 48. Flow - async Flow() .async { sync in … sync { /* 1*/ } … } .async { sync in … sync { /* 2 */ } … } .start()
  49. 49. var res1: Response!; var res2: Response! looper.invoke { dsl in fetch1 { r in res1 = r } dsl.isInfinity = true dsl.block = { item in if res1 != nil { section1.render(with: res1); item.isStop = true } } }.next { dsl in fetch2 { r in res2 = r } dsl.isInfinity = true dsl.block = { item in if res2 != nil { section2.render(with: res2); item.isStop = true } } } Flow() .async { sync in fetch1 { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .start()
  50. 50. class Flow { private class Block { static let TypeAsync = “async" var type: String let body: Any init(type: String, body: Any) { self.type = type self.body = body } } typealias VoidClosure = () -> Void typealias Sync = (@escaping VoidClosure) -> Void typealias Async = (@escaping Sync) -> Void private var blocks = [Block]() private var seq: Looper.Sequence? … }
  51. 51. class Flow { private class Block { … } typealias VoidClosure = () -> Void typealias Sync = (@escaping VoidClosure) -> Void typealias Async = (@escaping Sync) -> Void private var blocks = [Block]() privatevar seq: Looper.Sequence? @discardableResult func async(body: @escaping Async) -> Flow { blocks.append(Block(type: Block.TypeAsync, body: body)) return self } func start() { … _start() } … } private func _start() { while blocks.count > 0 { let block = blocks.removeFirst() switch block.type { case Block.TypeAsync: let body = block.body as! Async let dslBlock = getDSLBlock(body) seq = seq?.next(dslBlock) ?? looper.invoke(dslBlock) default: break } } }
  52. 52. class Flow { … typealias VoidClosure = () -> Void typealias Sync = (@escaping VoidClosure) -> Void typealias Async = (@escaping Sync) -> Void … private func getDSLBlock( _ async: @escaping Async ) -> (Looper.Looper.ItemDSL) -> Void { { dsl in var completion: VoidClosure? async({ c in completion = c }) dsl.isInfinity = true dsl.block = { item in if completion != nil { completion?(); item.isStop = true } } } } Flow() .async { sync in fetch1 { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .start()
  53. 53. Flow - async Flow() .async { sync in fetch1 { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .start()
  54. 54. Flow - async Flow() .async { sync in sync { indicator.show() } } .async { sync in fetch1 { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .async { sync in sync { indicator.hide() } } .start()
  55. 55. Flow - sync Flow() . .async { sync in fetch1 { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } . .start() .async { sync in sync { indicator.show() } } .async { sync in sync { indicator.hide() } }
  56. 56. Flow - sync class Flow { private class Block { static let TypeSync = "sync" … } …. @discardableResult func sync(body: @escaping Sync) -> Flow { blocks.append(Block(type: Block.TypeSync, body: body)) return self } … }
  57. 57. private func _start() { while blocks.count > 0 { let block = blocks.removeFirst() switch block.type { case Block.TypeAsync: let body = block.body as! Async let dslBlock = getDSLBlock(body) seq = seq?.next(dslBlock) ?? looper.invoke(dslBlock) case Block.Sync: let body = block.body as! VoidClosure let nBody: Async = { $0(body) } blocks.insert( Flow.Block(type: Block.TypeAsync, body: nBody), at: 0 ) default: break } } }
  58. 58. Flow() .sync { indicator.show() } .async { sync in fetch1 { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .sync { indicator.hide() } .start() 200ms + 15ms800ms + 15ms800ms + 15ms + 15ms800ms + 15ms + 15ms + 15ms Flow - sync 15ms=
  59. 59. Flow - pause Flow() .sync { indicator.show(); after(delay: 2) { indicator.hide() } } .async { sync in fetch1 { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .start()
  60. 60. Flow - pause Flow() .sync { indicator.show() } .async { sync in fetch1 { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .sync { indicator.hide() } .start()
  61. 61. Flow - pause var started: TimeInterval! Flow() .sync { indicator.show(); started = now() } .async { sync in fetch1 { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } .start()
  62. 62. Flow - pause private class Block { … static let TypePause = "pause" var delay: () -> TimeInterval init( type: String, body: Any, delay: @escaping () -> TimeInterval = { 0 } ) { … self.delay = delay } }
  63. 63. Flow - pause class Flow { … @discardableResult func pause( duration delay: @escaping () -> TimeInterval, body: @escaping VoidClosure = Block.EmptyBody ) -> Flow { blocks.append(Flow.Block(type: Block.TypePause, body: body, delay: delay)) return self } … }
  64. 64. private func getDSLBlock( delay: @escaping () -> TimeInterval = { 0 }, _ body: @escaping Async ) -> (Looper.Looper.ItemDSL) -> Void { { dsl in var waiting: Double! var started: Double! var completion: VoidClosure? body({ c in completion = c }) dsl.isInfinity = true dsl.block = { item in if started == nil { started = item.current; waiting = delay() } if completion != nil && item.current - started >= waiting { completion?(); item.isStop = true } } } } private func getDSLBlock( delay: @escaping () -> TimeInterval = { 0 }, _ body: @escaping Async ) -> (Looper.Looper.ItemDSL) -> Void { { dsl in var waiting: Double! var started: Double! var completion: VoidClosure? body({ c in completion = c }) dsl.isInfinity = true dsl.block = { item in if started == nil { started = item.current; waiting = delay() } if completion != nil { completion?(); item.isStop = true } } } } private func getDSLBlock( delay: @escaping () -> TimeInterval = { 0 }, _ body: @escaping Async ) -> (Looper.Looper.ItemDSL) -> Void { { dsl in var waiting: Double! var started: Double! var completion: VoidClosure? body({ c in completion = c }) dsl.isInfinity = true dsl.block = { item in if completion != nil { completion?(); item.isStop = true } } } } private func getDSLBlock( delay: @escaping () -> TimeInterval = { 0 }, _ body: @escaping Async ) -> (Looper.Looper.ItemDSL) -> Void { { dsl in var completion: VoidClosure? body({ c in completion = c }) dsl.isInfinity = true dsl.block = { item in if completion != nil { completion?(); item.isStop = true } } } } private func getDSLBlock( _ body: @escaping Async ) -> (Looper.Looper.ItemDSL) -> Void { { dsl in var completion: VoidClosure? body({ c in completion = c }) dsl.isInfinity = true dsl.block = { item in if completion != nil { completion?(); item.isStop = true } } } }
  65. 65. private func _start() { while blocks.count > 0 { let block = blocks.removeFirst() switch block.type { … case Block.Sync: let body = block.body as! VoidClosure let nBody: Async = { $0(body) } blocks.insert(Flow.Block(type: Block.TypeAsync, body: nBody), at: 0) case Block.Pause: let body = block.body as! VoidClosure let nBody: Async = { $0(body) } blocks.insert( Flow.Block(type: Block.TypeAsync, body: nBody, delay: block.delay), at: 0 ) default: break } } }
  66. 66. private func _start() { while blocks.count > 0 { let block = blocks.removeFirst() switch block.type { … case Block.Sync, Block.Pause: let body = block.body as! VoidClosure let nBody: Async = { $0(body) } blocks.insert( Flow.Block(type: Block.TypeAsync, body: nBody, delay: block.delay), at: 0 ) default: break } } }
  67. 67. private func _start() { while blocks.count > 0 { let block = blocks.removeFirst() switch block.type { case Block.TypeAsync: let delay = block.delay let body = block.body as! Async let dslBlock = getDSLBlock(delay: delay, body) seq = seq?.next(dslBlock) ?? looper.invoke(dslBlock) case Block.Sync, Block.Pause: let body = block.body as! VoidClosure let nBody: Async = { $0(body) } blocks.insert( Flow.Block(type: Block.TypeAsync, body: nBody, delay: block.delay), at: 0 ) default: break } } }
  68. 68. Flow - bundle var started: TimeInterval! Flow() .sync { indicator.show(); started = now() } .async { sync in fetch1 { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } .start()
  69. 69. Flow - bundle var started: TimeInterval! var k1: String!; var k2: String!; Flow() .sync { indicator.show(); started = now() } .async { sync in fetchKey1 { r in sync { key1 = r } } } .async { sync in fetchKey2 { r in sync { key2 = r) } } } .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } .start()
  70. 70. Flow - bundle Flow() .sync { indicator.show(); started = now() } .async { sync in fetchKey1 { r in sync { key1 = r } } } .async { sync in fetchKey2 { r in sync { key2 = r) } } } .sync { Flow() .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } … .start() } .start()
  71. 71. Flow - bundle Flow() .sync { indicator.show(); started = now() } .bundle { Flow().async { … }.async { … } } .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } .start()
  72. 72. Flow - bundle class Flow { private class Block { static let TypeBundle = “bundle" … } typealias Bundle = () -> Flow … @discardableResult func bundle(body: @escaping Bundle) -> Flow { blocks.append(Block(type: Block.TypeBundle, body: body)) return self } }
  73. 73. private func _start() { var sub: Flow? knitting: while blocks.count > 0 { let block = blocks.removeFirst() switch block.type { … case Block.TypeBundle: let body = block.body as! Bundle sub = body() break knitting default: break } } … }
  74. 74. private func _start() { var sub: Flow? … if let sub = sub { if blocks.count > 0 { sub.blocks.append( Flow.Block( type: Block.TypeBundle, body: { () -> Flow in self.seq = nil; return self } ) ) } if seq?.current != nil { seq?.current?.ended = { _ in sub._start() } } else { sub._start() } } }
  75. 75. Flow() .sync { indicator.show(); started = now() } .bundle { Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r } } } } .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } .start()
  76. 76. Flow() .sync { indicator.show(); started = now() } .bundle { Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r } } } } .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } .start() var flow1: Flow Flow() .sync { indicator.show(); started = now() } .bundle { Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r } } } } .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() }
  77. 77. var flow1: Flow flow1 = Flow() flow1.sync { indicator.show(); started = now() } .bundle { Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r } } } } .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() }
  78. 78. var flow1: Flow flow1 = Flow() flow1.sync { indicator.show(); started = now() } .bundle { Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r } } } .bundle { flow1 } } .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() }
  79. 79. var flow1: Flow flow1 = Flow() flow1.sync { indicator.show(); started = now() } .bundle { Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r } } } .bundle { flow1 .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } } }
  80. 80. var flow1: Flow flow1 = Flow() flow1.sync { indicator.show(); started = now() } Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r } } } .bundle { flow1 .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } }
  81. 81. var flow1: Flow flow1 = Flow() flow1.sync { indicator.show(); started = now() Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r } } } .bundle { flow1 .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } } .start() }
  82. 82. var flow1: Flow flow1 = Flow() flow1.sync { indicator.show(); started = now() Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r } } } .bundle { flow1 .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } } .start() }
  83. 83. var flow1: Flow flow1 = Flow() flow1.sync { indicator.show(); started = now() Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r } } } .bundle { flow1 .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } } .start() }
  84. 84. var flow1: Flow flow1 = Flow() Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r } } } .bundle { flow1 .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } }
  85. 85. var flow1: Flow flow1 = Flow() Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r } } } .bundle { flow1 .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } }
  86. 86. var flow1: Flow flow1 = Flow() Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r } } } flow1 .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() }
  87. 87. Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r flow1 .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } .start() } } }
  88. 88. private func _start() { var sub: Flow? … if let sub = sub { if blocks.count > 0 { sub.blocks.append( Flow.Block( type: Block.TypeBundle, body: { () -> Flow in self.seq = nil; return self } ) ) } if seq?.current != nil { seq?.current?.ended = { _ in sub._start() } } else { sub._start() } } }
  89. 89. Flow() .sync { indicator.show(); started = now() } .bundle { Flow() .async { sync in fetchKey1 { r in sync { k1 = r } } } .async { sync in fetchKey2 { r in sync { k2 = r } } } } .async { sync in fetch1(k1, k2) { r in sync { section1.render(with: r) } } } .async { sync in fetch2 { r in sync { section2.render(with: r) } } } .async { sync in fetch3 { r in sync { section3.render(with: r) } } } .pause(duration: { 2 - min(2, now() - started) }) { indicator.hide() } .start()
  90. 90. Q&A

×