SlideShare a Scribd company logo
1 of 96
Download to read offline
Animation in iOS

Looper



Code : https://github.com/dotNetTree/letswift2019
Animation in iOS
(0, 0) (40, 0)
(0, 0) (40, 0)
• 1 center (0, 0) -> center (40, 0)
(0, 0) (40, 0)
• 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)
}
• 1 center (0, 0) -> center(40, 0)
(0, 0) (40, 0)
vCircleCenterX.constant = 40
UIView.animate(withDuration: 1) {
vCircle.superview?.layoutIfNeeded()
}
(100, 100)
(100, 50)
(100, 100)
(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)
• 3 center (0, 0) -> center (40, 0) 

• 1.5 alpha 0.9 -> alpha 0.3
(0, 0) (40, 0)
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)
}
})
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)
}
})
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
}
• (Refresh Rate)

• 1


• Hz (60Hz = 1 60 )
• FPS (Frame Per Second)

• - 

• Hz FPS 

• (Device)
• CADisplayLink
• CADisplayLink

• A timer object that allows your application to
synchronize its drawing to the refresh rate of the
display
• CADisplayLink

• A timer object that allows your application to
synchronize its drawing to the refresh rate of the
display

•
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
}
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)

}
Looper
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)

}
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
…

}
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)

}
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 { … }

}

}
looper.invoke { (dsl) in

dsl.time = 0.3

dsl.block = { item in

vCircle.alpha = CGFloat(item.rate)

}

}
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

}
Updater
private var displayLink: CADisplayLink?

func aniStart() {

displayLink = CADisplayLink(target: self, selector: #selector(update))

displayLink?.add(to: .main, forMode: .common)

… 

}

@objc func update() { ... }
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() } }

}
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
Demo
Demo
Code : https://github.com/dotNetTree/letswift2019
Demo
Code : https://github.com/dotNetTree/letswift2019
Looper
Invokeitem
loop
Items
Pool
item
Updater
update
Section 1
Section 2
Section 3
Section 1
Section 2
Section 3
Section 1
Section 2
Section 3
Section 1
Section 2
Section 3
API 1
API 2
API 3
fetch 1
fetch 2
fetch 3
0.2sec
0.8sec
0.3sec
Section 1
Section 2Section 3
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)

}
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)

}
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)

}
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 }

}

}
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
Flow()

.async { sync in
/* 

start 

*/

}

.async { sync in
/* 

start 

*/

}

.start()
Flow - async
Flow()

.async { sync in
/* 

start 

*/

}

.async { sync in
/* 

start 

*/

}

.start()
Flow - async
Flow()

.async { sync in
…

sync { /* 1*/ }

…

}

.async { sync in
…

sync { /* 2 */ }

…

}

.start()
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()
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?

…

}
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
}

}

}
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()
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()
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()
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() }
}
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

}

…

}
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
}

}

}
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=
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()
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()
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()
Flow - pause
private class Block {

…
static let TypePause = "pause"

var delay: () -> TimeInterval

init(

type: String, 

body: Any, 

delay: @escaping () -> TimeInterval = { 0 }

) {

…

self.delay = delay

}

}
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

}

…

}
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 

}

}

}

}
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
}

}

}
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
}

}

}
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
}

}

}
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()
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()
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()
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()
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

}

}
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
}

}

…

}
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() }

}

}
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()
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() 

}
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() 

}
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() 

}
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() 

}

}

}
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() 

}

}
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()

}
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()

}
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()

}
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() 

}

}
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() 

}

}
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() 

}
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()

} } }
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() }

}

}
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()
Q&A
미려한 UI/UX를 위한 여정

More Related Content

What's hot

Python Performance 101
Python Performance 101Python Performance 101
Python Performance 101
Ankur Gupta
 
Jscex: Write Sexy JavaScript
Jscex: Write Sexy JavaScriptJscex: Write Sexy JavaScript
Jscex: Write Sexy JavaScript
jeffz
 
Jscex: Write Sexy JavaScript (中文)
Jscex: Write Sexy JavaScript (中文)Jscex: Write Sexy JavaScript (中文)
Jscex: Write Sexy JavaScript (中文)
jeffz
 

What's hot (20)

ECMAScript 6
ECMAScript 6ECMAScript 6
ECMAScript 6
 
ES2015 (ES6) Overview
ES2015 (ES6) OverviewES2015 (ES6) Overview
ES2015 (ES6) Overview
 
Programmation fonctionnelle en JavaScript
Programmation fonctionnelle en JavaScriptProgrammation fonctionnelle en JavaScript
Programmation fonctionnelle en JavaScript
 
Emerging Languages: A Tour of the Horizon
Emerging Languages: A Tour of the HorizonEmerging Languages: A Tour of the Horizon
Emerging Languages: A Tour of the Horizon
 
Swift rocks! #1
Swift rocks! #1Swift rocks! #1
Swift rocks! #1
 
Swift Rocks #2: Going functional
Swift Rocks #2: Going functionalSwift Rocks #2: Going functional
Swift Rocks #2: Going functional
 
Python Performance 101
Python Performance 101Python Performance 101
Python Performance 101
 
响应式编程及框架
响应式编程及框架响应式编程及框架
响应式编程及框架
 
EcmaScript 6
EcmaScript 6 EcmaScript 6
EcmaScript 6
 
The Ring programming language version 1.5.3 book - Part 10 of 184
The Ring programming language version 1.5.3 book - Part 10 of 184The Ring programming language version 1.5.3 book - Part 10 of 184
The Ring programming language version 1.5.3 book - Part 10 of 184
 
Continuation Passing Style and Macros in Clojure - Jan 2012
Continuation Passing Style and Macros in Clojure - Jan 2012Continuation Passing Style and Macros in Clojure - Jan 2012
Continuation Passing Style and Macros in Clojure - Jan 2012
 
The Ring programming language version 1.5.4 book - Part 10 of 185
The Ring programming language version 1.5.4 book - Part 10 of 185The Ring programming language version 1.5.4 book - Part 10 of 185
The Ring programming language version 1.5.4 book - Part 10 of 185
 
Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...
Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...
Java/Scala Lab: Анатолий Кметюк - Scala SubScript: Алгебра для реактивного пр...
 
Jscex: Write Sexy JavaScript
Jscex: Write Sexy JavaScriptJscex: Write Sexy JavaScript
Jscex: Write Sexy JavaScript
 
Rainer Grimm, “Functional Programming in C++11”
Rainer Grimm, “Functional Programming in C++11”Rainer Grimm, “Functional Programming in C++11”
Rainer Grimm, “Functional Programming in C++11”
 
Jscex: Write Sexy JavaScript (中文)
Jscex: Write Sexy JavaScript (中文)Jscex: Write Sexy JavaScript (中文)
Jscex: Write Sexy JavaScript (中文)
 
RxJS 5 in Depth
RxJS 5 in DepthRxJS 5 in Depth
RxJS 5 in Depth
 
連邦の白いヤツ 「Objective-C」
連邦の白いヤツ 「Objective-C」連邦の白いヤツ 「Objective-C」
連邦の白いヤツ 「Objective-C」
 
ITT 2015 - Saul Mora - Object Oriented Function Programming
ITT 2015 - Saul Mora - Object Oriented Function ProgrammingITT 2015 - Saul Mora - Object Oriented Function Programming
ITT 2015 - Saul Mora - Object Oriented Function Programming
 
SDC - Einführung in Scala
SDC - Einführung in ScalaSDC - Einführung in Scala
SDC - Einführung in Scala
 

Similar to 미려한 UI/UX를 위한 여정

DjangoCon US 2011 - Monkeying around at New Relic
DjangoCon US 2011 - Monkeying around at New RelicDjangoCon US 2011 - Monkeying around at New Relic
DjangoCon US 2011 - Monkeying around at New Relic
Graham Dumpleton
 
Djangocon11: Monkeying around at New Relic
Djangocon11: Monkeying around at New RelicDjangocon11: Monkeying around at New Relic
Djangocon11: Monkeying around at New Relic
New Relic
 

Similar to 미려한 UI/UX를 위한 여정 (20)

Monadologie
MonadologieMonadologie
Monadologie
 
Swift 함수 커링 사용하기
Swift 함수 커링 사용하기Swift 함수 커링 사용하기
Swift 함수 커링 사용하기
 
Corona sdk
Corona sdkCorona sdk
Corona sdk
 
DjangoCon US 2011 - Monkeying around at New Relic
DjangoCon US 2011 - Monkeying around at New RelicDjangoCon US 2011 - Monkeying around at New Relic
DjangoCon US 2011 - Monkeying around at New Relic
 
Djangocon11: Monkeying around at New Relic
Djangocon11: Monkeying around at New RelicDjangocon11: Monkeying around at New Relic
Djangocon11: Monkeying around at New Relic
 
Is java8a truefunctionallanguage
Is java8a truefunctionallanguageIs java8a truefunctionallanguage
Is java8a truefunctionallanguage
 
Is java8 a true functional programming language
Is java8 a true functional programming languageIs java8 a true functional programming language
Is java8 a true functional programming language
 
Compact and safely: static DSL on Kotlin
Compact and safely: static DSL on KotlinCompact and safely: static DSL on Kotlin
Compact and safely: static DSL on Kotlin
 
Scala by Luc Duponcheel
Scala by Luc DuponcheelScala by Luc Duponcheel
Scala by Luc Duponcheel
 
The Ring programming language version 1.6 book - Part 62 of 189
The Ring programming language version 1.6 book - Part 62 of 189The Ring programming language version 1.6 book - Part 62 of 189
The Ring programming language version 1.6 book - Part 62 of 189
 
Go: It's Not Just For Google
Go: It's Not Just For GoogleGo: It's Not Just For Google
Go: It's Not Just For Google
 
ScalaDays 2014 - Reactive Scala 3D Game Engine
ScalaDays 2014 - Reactive Scala 3D Game Engine ScalaDays 2014 - Reactive Scala 3D Game Engine
ScalaDays 2014 - Reactive Scala 3D Game Engine
 
The Ring programming language version 1.9 book - Part 71 of 210
The Ring programming language version 1.9 book - Part 71 of 210The Ring programming language version 1.9 book - Part 71 of 210
The Ring programming language version 1.9 book - Part 71 of 210
 
Effective Java with Groovy & Kotlin How Languages Influence Adoption of Good ...
Effective Java with Groovy & Kotlin How Languages Influence Adoption of Good ...Effective Java with Groovy & Kotlin How Languages Influence Adoption of Good ...
Effective Java with Groovy & Kotlin How Languages Influence Adoption of Good ...
 
Coding in Style
Coding in StyleCoding in Style
Coding in Style
 
Introduction to kotlin
Introduction to kotlinIntroduction to kotlin
Introduction to kotlin
 
Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with Groovy
 
The Ring programming language version 1.5.4 book - Part 60 of 185
The Ring programming language version 1.5.4 book - Part 60 of 185The Ring programming language version 1.5.4 book - Part 60 of 185
The Ring programming language version 1.5.4 book - Part 60 of 185
 
The Ring programming language version 1.10 book - Part 81 of 212
The Ring programming language version 1.10 book - Part 81 of 212The Ring programming language version 1.10 book - Part 81 of 212
The Ring programming language version 1.10 book - Part 81 of 212
 
Scala coated JVM
Scala coated JVMScala coated JVM
Scala coated JVM
 

Recently uploaded

The basics of sentences session 3pptx.pptx
The basics of sentences session 3pptx.pptxThe basics of sentences session 3pptx.pptx
The basics of sentences session 3pptx.pptx
heathfieldcps1
 

Recently uploaded (20)

Mehran University Newsletter Vol-X, Issue-I, 2024
Mehran University Newsletter Vol-X, Issue-I, 2024Mehran University Newsletter Vol-X, Issue-I, 2024
Mehran University Newsletter Vol-X, Issue-I, 2024
 
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
 
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
 
Kodo Millet PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...
Kodo Millet  PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...Kodo Millet  PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...
Kodo Millet PPT made by Ghanshyam bairwa college of Agriculture kumher bhara...
 
How to setup Pycharm environment for Odoo 17.pptx
How to setup Pycharm environment for Odoo 17.pptxHow to setup Pycharm environment for Odoo 17.pptx
How to setup Pycharm environment for Odoo 17.pptx
 
COMMUNICATING NEGATIVE NEWS - APPROACHES .pptx
COMMUNICATING NEGATIVE NEWS - APPROACHES .pptxCOMMUNICATING NEGATIVE NEWS - APPROACHES .pptx
COMMUNICATING NEGATIVE NEWS - APPROACHES .pptx
 
Understanding Accommodations and Modifications
Understanding  Accommodations and ModificationsUnderstanding  Accommodations and Modifications
Understanding Accommodations and Modifications
 
21st_Century_Skills_Framework_Final_Presentation_2.pptx
21st_Century_Skills_Framework_Final_Presentation_2.pptx21st_Century_Skills_Framework_Final_Presentation_2.pptx
21st_Century_Skills_Framework_Final_Presentation_2.pptx
 
The basics of sentences session 3pptx.pptx
The basics of sentences session 3pptx.pptxThe basics of sentences session 3pptx.pptx
The basics of sentences session 3pptx.pptx
 
HMCS Vancouver Pre-Deployment Brief - May 2024 (Web Version).pptx
HMCS Vancouver Pre-Deployment Brief - May 2024 (Web Version).pptxHMCS Vancouver Pre-Deployment Brief - May 2024 (Web Version).pptx
HMCS Vancouver Pre-Deployment Brief - May 2024 (Web Version).pptx
 
How to Create and Manage Wizard in Odoo 17
How to Create and Manage Wizard in Odoo 17How to Create and Manage Wizard in Odoo 17
How to Create and Manage Wizard in Odoo 17
 
Wellbeing inclusion and digital dystopias.pptx
Wellbeing inclusion and digital dystopias.pptxWellbeing inclusion and digital dystopias.pptx
Wellbeing inclusion and digital dystopias.pptx
 
ICT Role in 21st Century Education & its Challenges.pptx
ICT Role in 21st Century Education & its Challenges.pptxICT Role in 21st Century Education & its Challenges.pptx
ICT Role in 21st Century Education & its Challenges.pptx
 
Single or Multiple melodic lines structure
Single or Multiple melodic lines structureSingle or Multiple melodic lines structure
Single or Multiple melodic lines structure
 
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptxHMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
 
Accessible Digital Futures project (20/03/2024)
Accessible Digital Futures project (20/03/2024)Accessible Digital Futures project (20/03/2024)
Accessible Digital Futures project (20/03/2024)
 
Jamworks pilot and AI at Jisc (20/03/2024)
Jamworks pilot and AI at Jisc (20/03/2024)Jamworks pilot and AI at Jisc (20/03/2024)
Jamworks pilot and AI at Jisc (20/03/2024)
 
Plant propagation: Sexual and Asexual propapagation.pptx
Plant propagation: Sexual and Asexual propapagation.pptxPlant propagation: Sexual and Asexual propapagation.pptx
Plant propagation: Sexual and Asexual propapagation.pptx
 
This PowerPoint helps students to consider the concept of infinity.
This PowerPoint helps students to consider the concept of infinity.This PowerPoint helps students to consider the concept of infinity.
This PowerPoint helps students to consider the concept of infinity.
 
80 ĐỀ THI THỬ TUYỂN SINH TIẾNG ANH VÀO 10 SỞ GD – ĐT THÀNH PHỐ HỒ CHÍ MINH NĂ...
80 ĐỀ THI THỬ TUYỂN SINH TIẾNG ANH VÀO 10 SỞ GD – ĐT THÀNH PHỐ HỒ CHÍ MINH NĂ...80 ĐỀ THI THỬ TUYỂN SINH TIẾNG ANH VÀO 10 SỞ GD – ĐT THÀNH PHỐ HỒ CHÍ MINH NĂ...
80 ĐỀ THI THỬ TUYỂN SINH TIẾNG ANH VÀO 10 SỞ GD – ĐT THÀNH PHỐ HỒ CHÍ MINH NĂ...
 

미려한 UI/UX를 위한 여정

  • 1.
  • 2. Animation in iOS Looper Code : https://github.com/dotNetTree/letswift2019
  • 4.
  • 5.
  • 8. • 1 center (0, 0) -> center (40, 0) (0, 0) (40, 0)
  • 9. • 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) }
  • 10. • 1 center (0, 0) -> center(40, 0) (0, 0) (40, 0) vCircleCenterX.constant = 40 UIView.animate(withDuration: 1) { vCircle.superview?.layoutIfNeeded() }
  • 11.
  • 14. (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)
  • 15. • 3 center (0, 0) -> center (40, 0) • 1.5 alpha 0.9 -> alpha 0.3 (0, 0) (40, 0)
  • 16. 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) } })
  • 17. 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) } })
  • 18. 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 }
  • 19. • (Refresh Rate) • 1 • Hz (60Hz = 1 60 )
  • 20. • FPS (Frame Per Second) • - • Hz FPS • (Device)
  • 22. • CADisplayLink • A timer object that allows your application to synchronize its drawing to the refresh rate of the display
  • 23. • CADisplayLink • A timer object that allows your application to synchronize its drawing to the refresh rate of the display •
  • 24. 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 }
  • 25. 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) }
  • 27. 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) }
  • 28. 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 … }
  • 29. 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) }
  • 30. 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 { … } } }
  • 31. looper.invoke { (dsl) in dsl.time = 0.3 dsl.block = { item in vCircle.alpha = CGFloat(item.rate) } }
  • 32. 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 }
  • 33. Updater private var displayLink: CADisplayLink? func aniStart() { displayLink = CADisplayLink(target: self, selector: #selector(update)) displayLink?.add(to: .main, forMode: .common) … } @objc func update() { ... }
  • 34. 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() } } }
  • 35. 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
  • 36. Demo
  • 40.
  • 44. Section 1 Section 2 Section 3 API 1 API 2 API 3 fetch 1 fetch 2 fetch 3 0.2sec 0.8sec 0.3sec
  • 46. 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) }
  • 47. 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) }
  • 48. 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) }
  • 49. 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 } } }
  • 50. 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 } } }
  • 51. Flow - async Flow() .async { sync in /* start */ } .async { sync in /* start */ } .start()
  • 52. Flow - async Flow() .async { sync in /* start */ } .async { sync in /* start */ } .start()
  • 53. Flow - async Flow() .async { sync in … sync { /* 1*/ } … } .async { sync in … sync { /* 2 */ } … } .start()
  • 54. 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()
  • 55. 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? … }
  • 56. 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 } } }
  • 57. 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()
  • 58. 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()
  • 59. 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()
  • 60. 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() } }
  • 61. 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 } … }
  • 62. 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 } } }
  • 63. 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=
  • 64. 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()
  • 65. 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()
  • 66. 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()
  • 67. Flow - pause private class Block { … static let TypePause = "pause" var delay: () -> TimeInterval init( type: String, body: Any, delay: @escaping () -> TimeInterval = { 0 } ) { … self.delay = delay } }
  • 68. 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 } … }
  • 69. 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 } } } }
  • 70. 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 } } }
  • 71. 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 } } }
  • 72. 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 } } }
  • 73. 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()
  • 74. 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()
  • 75. 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()
  • 76. 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()
  • 77. 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 } }
  • 78. 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 } } … }
  • 79. 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() } } }
  • 80. 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()
  • 81. 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() }
  • 82. 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() }
  • 83. 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() }
  • 84. 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() } } }
  • 85. 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() } }
  • 86. 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() }
  • 87. 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() }
  • 88. 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() }
  • 89. 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() } }
  • 90. 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() } }
  • 91. 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() }
  • 92. 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() } } }
  • 93. 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() } } }
  • 94. 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()
  • 95. Q&A