├── AsyncOperations.playground
├── Contents.swift
├── Sources
│ ├── SlowSum.swift
│ └── imageLoad.swift
├── contents.xcplayground
├── playground.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── tatianakornilova.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── timeline.xctimeline
├── Cancellation.playground
├── Contents.swift
├── Sources
│ ├── DelayUtil.swift
│ ├── SlowSum.swift
│ └── StopClock.swift
├── contents.xcplayground
├── playground.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── tatianakornilova.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── timeline.xctimeline
├── CancellationFourImages.playground
├── Contents.swift
├── Sources
│ ├── FourImages.swift
│ ├── GradientGenerator.swift
│ ├── ImageProvider.swift
│ ├── Operations
│ │ ├── Abstract
│ │ │ ├── AsyncOperation.swift
│ │ │ └── ImageTakeOperation.swift
│ │ ├── Filter.swift
│ │ ├── ImageLoadOperation.swift
│ │ ├── ImageOutputOperation.swift
│ │ └── PostProcessingImage.swift
│ ├── PostProcess.swift
│ ├── StopClock.swift
│ └── UIImageExtensions.swift
├── contents.xcplayground
├── playground.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── tatianakornilova.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── timeline.xctimeline
├── CancellationGroup.playground
├── Contents.swift
├── Sources
│ ├── DelayUtil.swift
│ ├── SlowSum.swift
│ └── StopClock.swift
├── contents.xcplayground
├── playground.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── tatianakornilova.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── timeline.xctimeline
├── LoadAndFilter.playground
├── Contents.swift
├── Resources
│ ├── dark_road_small.jpg
│ ├── train_day.jpg
│ ├── train_dusk.jpg
│ └── train_night.jpg
├── Sources
│ ├── AsyncOperation.swift
│ ├── Duration.swift
│ ├── Filter.swift
│ ├── GradientGenerator.swift
│ ├── ImageLoad.swift
│ └── UIImage+Blur.swift
├── contents.xcplayground
├── playground.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── tatianakornilova.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── timeline.xctimeline
├── Operation.playground
├── Contents.swift
├── Resources
│ └── dark_road_small.jpg
├── Sources
│ ├── Duration.swift
│ ├── Filter.swift
│ ├── GradientGenerator.swift
│ └── UIImageExtensions.swift
├── contents.xcplayground
├── playground.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── tatianakornilova.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── timeline.xctimeline
├── OperationQueue.playground
├── Contents.swift
├── Resources
│ ├── city.jpg
│ ├── dark_road.jpg
│ ├── train_day.jpg
│ ├── train_dusk.jpg
│ └── train_night.jpg
├── Sources
│ ├── Duration.swift
│ ├── Filter.swift
│ ├── GradientGenerator.swift
│ ├── StopClock.swift
│ └── UIImageExtensions.swift
├── contents.xcplayground
├── playground.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── tatianakornilova.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── timeline.xctimeline
└── OperationTableViewController
├── GCDTableViewController.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── tatianakornilova.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ └── tatianakornilova.xcuserdatad
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ ├── GCDTableViewController.xcscheme
│ └── xcschememanagement.plist
└── GCDTableViewController
├── AppDelegate.swift
├── Assets.xcassets
└── AppIcon.appiconset
│ └── Contents.json
├── AsyncOperation.swift
├── Base.lproj
├── LaunchScreen.storyboard
└── Main.storyboard
├── Filter.swift
├── GradientGenerator.swift
├── ImageLoadOperation.swift
├── ImageOutputOperation.swift
├── ImageProvider.swift
├── ImageTableViewCell.swift
├── ImageTableViewController.swift
├── ImageTakeOperation.swift
├── Info.plist
├── PostProcess.swift
├── PostProcessingImage.swift
└── UIImageExtensions.swift
/AsyncOperations.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import PlaygroundSupport
3 | PlaygroundPage.current.needsIndefiniteExecution = true
4 | //: ## AsyncOperation: Превращение Asynchronous Function в Operation
5 | /*:
6 | Для СИНХРОННЫХ задач мы можем создать subclass `Operation` путем переопределения метода `main()`.
7 |
8 | `AsyncOperation` - это пользовательский subclass `Operation`, который управляет изменением состояния операции автоматически. Для того, чтобы вы смогли воспользоваться этим subclass `AsyncOperation` и превратить свою АСИНХРОННУЮ функцию в `Operation`, вам нужно:
9 |
10 | 1. Создать subclass класса `AsyncOperation`.
11 | 2. Переопределить (override) `main()` и вызвать свою АСИНХРОННУЮ функцию.
12 | 3. Изменить значение свойства `state` вашего subclass `AsyncOperation` на `.finished` в асинхронном callback.
13 |
14 | - ВАЖНО:
15 | Шаг 3 этой инструкции является *чрезвычайно* важным - именно так вы сообщаете очереди операций `OperationQueue`, что запущенная операция закончилась. В противном случае она никогда не закончится.
16 | */
17 | class AsyncOperation: Operation {
18 |
19 | // Определяем перечисление enum State со свойством keyPath
20 | enum State: String {
21 | case ready, executing, finished
22 |
23 | fileprivate var keyPath: String {
24 | return "is" + rawValue.capitalized
25 | }
26 | }
27 |
28 | // Помещаем в subclass свойство state типа State
29 | var state = State.ready {
30 | willSet {
31 | willChangeValue(forKey: newValue.keyPath)
32 | willChangeValue(forKey: state.keyPath)
33 | }
34 | didSet {
35 | didChangeValue(forKey: oldValue.keyPath)
36 | didChangeValue(forKey: state.keyPath)
37 | }
38 | }
39 | }
40 | /*:
41 | Каждое свойство состояния, наследуемое из класса `Operation` переопределяем таким образом, чтобы "подменить" их новым свойством `state`.
42 |
43 | Свойство `asynchronous` нужно установить в `true`, чтобы сообщить системе, что мы будем управлять состоянием операции вручную.
44 |
45 | Мы также вынуждены переопределить методы `start()` и `cancel()` для того, чтобы "имплантировать" новое свойство `state`.
46 | */
47 | extension AsyncOperation {
48 | // Переопределения для Operation
49 | override var isReady: Bool {
50 | return super.isReady && state == .ready
51 | }
52 |
53 | override var isExecuting: Bool {
54 | return state == .executing
55 | }
56 |
57 | override var isFinished: Bool {
58 | return state == .finished
59 | }
60 |
61 | override var isAsynchronous: Bool {
62 | return true
63 | }
64 |
65 | override func start() {
66 | if isCancelled {
67 | state = .finished
68 | return
69 | }
70 | main()
71 | state = .executing
72 | }
73 |
74 | override func cancel() {
75 | state = .finished
76 | }
77 |
78 | }
79 | /*:
80 | Теперь "обернуть" АСИНХРОННУЮ функцию в операцию `AsyncOperation` проще простого - переопределяем метод `main()`, не забывая установить свойству `state` в замыкании completion значение `.finished`:
81 | */
82 | class SumOperation: AsyncOperation {
83 | // Определяем: Properties, init, main
84 | let lhs: Int
85 | let rhs: Int
86 | var result: Int?
87 |
88 | init(lhs: Int, rhs: Int) {
89 | self.lhs = lhs
90 | self.rhs = rhs
91 | super.init()
92 | }
93 |
94 | override func main() {
95 | asyncAdd(lhs: lhs, rhs: rhs) { result in
96 | self.result = result
97 | self.state = .finished
98 | }
99 | }
100 | }
101 | //: Используем `OperationQueue` и массив пар чисел:
102 | let additionQueue = OperationQueue()
103 |
104 | let input = [(1,5), (5,8), (6,1), (3,9), (6,12), (1,0)]
105 |
106 | for (lhs, rhs) in input {
107 | // Создаем объект SumOperation
108 | let operation = SumOperation(lhs: lhs, rhs: rhs)
109 | operation.completionBlock = {
110 | guard let result = operation.result else { return }
111 | print("\(lhs) + \(rhs) = \(result)")
112 | }
113 |
114 | // Добавляем SumOperation на очередь additionQueue
115 | additionQueue.addOperation(operation)
116 | }
117 |
118 |
119 | class ImageLoadOperation: AsyncOperation {
120 | var url: URL?
121 | var outputImage: UIImage?
122 |
123 | init(url: URL?) {
124 | self.url = url
125 | super.init()
126 | }
127 |
128 | override func main() {
129 | if let imageURL = url {
130 | asyncImageLoad(imageURL: imageURL) { [unowned self] image in
131 | self.outputImage = image
132 | self.state = .finished
133 | }
134 | }
135 | }
136 | }
137 |
138 | //------------------------------------------------------------------------------
139 | var view = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
140 | var eiffelImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
141 | eiffelImage.backgroundColor = UIColor.yellow
142 | eiffelImage.contentMode = .scaleAspectFit
143 | view.addSubview(eiffelImage)
144 | //------------------------------------------------------------------------------
145 |
146 | PlaygroundPage.current.liveView = view
147 |
148 | let imageURL = URL(string:"http://www.planetware.com/photos-large/F/france-paris-eiffel-tower.jpg")
149 |
150 | // Создаем операцию загрузки изображения operationLoad
151 |
152 | let operationLoad = ImageLoadOperation(url: imageURL)
153 |
154 | operationLoad.completionBlock = {
155 | OperationQueue.main.addOperation {
156 | eiffelImage.image = operationLoad.outputImage
157 | }
158 | }
159 |
160 | // Добавляем operationLoad на очередь loadQueue
161 |
162 | let loadQueue = OperationQueue()
163 | loadQueue.addOperation(operationLoad)
164 | loadQueue.waitUntilAllOperationsAreFinished()
165 | sleep(5)
166 | operationLoad.outputImage
167 | //PlaygroundPage.current.finishExecution()
168 |
--------------------------------------------------------------------------------
/AsyncOperations.playground/Sources/SlowSum.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import Foundation
24 |
25 | public func slowAdd(_ input: (Int, Int)) -> Int {
26 | sleep(1)
27 | return input.0 + input.1
28 | }
29 |
30 | public func slowAddArray(_ input: [(Int, Int)], progress: ((Double) -> (Bool))? = nil) -> [Int] {
31 | var results = [Int]()
32 | for pair in input {
33 | results.append(slowAdd(pair))
34 | if let progress = progress {
35 | if !progress(Double(results.count) / Double(input.count)) { return results }
36 | }
37 | }
38 | return results
39 | }
40 |
41 | private let workerQueue = DispatchQueue(label: "com.raywenderlich.slowsum", attributes: DispatchQueue.Attributes.concurrent)
42 |
43 | public func asyncAdd_GCD(_ input: (Int, Int), completionQueue: DispatchQueue, completion:@escaping (Int) -> ()) {
44 | workerQueue.async {
45 | let result = slowAdd(input)
46 | completionQueue.async {
47 | completion(result)
48 | }
49 | }
50 | }
51 |
52 |
53 | public func asyncAdd(lhs: Int, rhs: Int, callback: @escaping (Int) -> ()) {
54 | let additionQueue = OperationQueue()
55 | additionQueue.addOperation {
56 | sleep(1)
57 | callback(lhs + rhs)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/AsyncOperations.playground/Sources/imageLoad.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | public func asyncImageLoad(imageURL: URL, completion:@escaping (UIImage?) -> () ) {
4 | let task = URLSession.shared.dataTask(with: imageURL){
5 | (data, response, error) in
6 | if let data = data {
7 | completion(UIImage(data: data))
8 | }
9 | }
10 | task.resume()
11 | }
12 |
--------------------------------------------------------------------------------
/AsyncOperations.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/AsyncOperations.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/AsyncOperations.playground/playground.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/AsyncOperations.playground/playground.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/AsyncOperations.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
14 |
15 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Cancellation.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import UIKit
3 | import PlaygroundSupport
4 | PlaygroundPage.current.needsIndefiniteExecution = true
5 | //: ## Operation Cancellation
6 | //: Операция `ArraySumOperation` берет массив кортежей `(Int, Int)`, использует функцию медленного сложения `slowAdd()`, представленную в __Sources__ и создает массив сумм чисел, составляющих кортеж.
7 | class ArraySumOperation: Operation {
8 | let inputArray: [(Int, Int)]
9 | var outputArray = [Int]()
10 |
11 | init(input: [(Int, Int)]) {
12 | inputArray = input
13 | super.init()
14 | }
15 |
16 | override func main() {
17 | for pair in inputArray {
18 | if isCancelled { return }
19 | outputArray.append (slowAdd(pair))
20 | }
21 | }
22 | }
23 | //: Другая операция `AnotherArraySumOperation` использует уже готовую функцию формирования из массива кортежей массива сумм чисел, составляющих кортеж
24 | class AnotherArraySumOperation: Operation {
25 | let inputArray: [(Int, Int)]
26 | var outputArray: [Int]?
27 |
28 | init(input: [(Int, Int)]) {
29 | inputArray = input
30 | super.init()
31 | }
32 |
33 | override func main() {
34 | // DONE: Fill this in
35 | outputArray = slowAddArray(inputArray) {
36 | progress in
37 | print("\(progress*100)% of the array processed")
38 | return !self.isCancelled
39 | }
40 | }
41 | }
42 | //: Входной массив
43 | let numberArray = [(1,2), (3,4), (5,6), (7,8), (9,10)]
44 | //: Операция `sumOperation` и очередь операций `queue`
45 | let sumOperation = AnotherArraySumOperation(input: numberArray)
46 | let queue = OperationQueue()
47 | //: Добавляем операцию в очередь операций и запускаем таймер. Таймер в дальнейшем поможет нам отрегулировать время "ожидания", спустя которое мы сможем проверить реакцию `sumOperation` на вызов метода `cancel()`.
48 | startClock()
49 | queue.addOperation(sumOperation)
50 | //: Немного "ожидаем" с помощью sleep(...), а затем удаляем операцию
51 | sleep(4)
52 | sumOperation.cancel()
53 |
54 | sumOperation.completionBlock = {
55 | stopClock()
56 | sumOperation.outputArray
57 | PlaygroundPage.current.finishExecution()
58 | }
59 |
--------------------------------------------------------------------------------
/Cancellation.playground/Sources/DelayUtil.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import Foundation
24 |
25 | public func delay(_ delay: Double, closure: @escaping () -> ()) {
26 | DispatchQueue.main.asyncAfter(
27 | deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC),
28 | execute: closure)
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/Cancellation.playground/Sources/SlowSum.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import Foundation
24 |
25 | public func slowAdd(_ input: (Int, Int)) -> Int {
26 | sleep(1)
27 | return input.0 + input.1
28 | }
29 |
30 | public func slowAddArray(_ input: [(Int, Int)],
31 | progress: ((Double) -> (Bool))? = nil) -> [Int] {
32 | var results = [Int]()
33 | for pair in input {
34 | results.append(slowAdd(pair))
35 | if let progress = progress {
36 | if !progress(Double(results.count) / Double(input.count)) { return results }
37 | }
38 | }
39 | return results
40 | }
41 |
42 | private let workerQueue = DispatchQueue(label: "com.raywenderlich.slowsum", attributes: DispatchQueue.Attributes.concurrent)
43 |
44 | public func asyncAdd_GCD(_ input: (Int, Int), completionQueue: DispatchQueue, completion:@escaping (Int) -> ()) {
45 | workerQueue.async {
46 | let result = slowAdd(input)
47 | completionQueue.async {
48 | completion(result)
49 | }
50 | }
51 | }
52 |
53 | private let additionQueue = OperationQueue()
54 | public func asyncAdd_OpQ(lhs: Int, rhs: Int, callback: @escaping (Int) -> ()) {
55 | additionQueue.addOperation {
56 | sleep(1)
57 | callback(lhs + rhs)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Cancellation.playground/Sources/StopClock.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import Foundation
24 |
25 | fileprivate var lastStartTime = Date()
26 |
27 | public func startClock(){
28 | lastStartTime = Date()
29 | }
30 |
31 | public func stopClock() -> TimeInterval {
32 | return Date().timeIntervalSince(lastStartTime)
33 | }
34 |
--------------------------------------------------------------------------------
/Cancellation.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Cancellation.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Cancellation.playground/playground.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/Cancellation.playground/playground.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Cancellation.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
14 |
15 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import PlaygroundSupport
3 | PlaygroundPage.current.needsIndefiniteExecution = true
4 |
5 | //: 'ImageProvider' - это обычный класс, который управляет очередью операций и набор операций, работающих на единую цель, представляющую собой граф зависимых операций, исполняемых одна за другой.
6 | class ImageProvider {
7 |
8 | private var operationQueue = OperationQueue ()
9 |
10 | init(imageURLString: String,completion: @escaping (UIImage?) -> ()) {
11 | operationQueue.isSuspended = true
12 | operationQueue.maxConcurrentOperationCount = 2
13 |
14 | guard let imageURL = URL(string: imageURLString) else {return}
15 |
16 | // Создаем операции
17 | let dataLoad = ImageLoadOperation(url: imageURL)
18 | let filter = Filter(image: nil)
19 | let postProcess = PostProcessImageOperation(image: nil)
20 | let output = ImageOutputOperation(){image in
21 | OperationQueue.main.addOperation {completion (image) }}
22 |
23 | let operations = [dataLoad, filter, postProcess, output]
24 |
25 | // Добавляем dependencies
26 | filter.addDependency(dataLoad)
27 | postProcess.addDependency(filter)
28 | output.addDependency(postProcess)
29 | operationQueue.addOperations(operations, waitUntilFinished: false)
30 | }
31 |
32 | func start() {
33 | operationQueue.isSuspended = false
34 | }
35 |
36 | func cancel() {
37 | operationQueue.cancelAllOperations()
38 | }
39 |
40 | func wait() {
41 | operationQueue.waitUntilAllOperationsAreFinished()
42 | }
43 | }
44 |
45 |
46 | var view = FourImages(frame: CGRect(x: 0, y: 0, width: 400, height: 400))
47 | view.backgroundColor = UIColor.red
48 |
49 | let imageURLs = ["http://www.planetware.com/photos-large/F/france-paris-eiffel-tower.jpg",
50 | "http://adriatic-lines.com/wp-content/uploads/2015/04/canal-of-Venice.jpg",
51 | "http://bestkora.com/IosDeveloper/wp-content/uploads/2016/12/Screen-Shot-2017-01-17-at-9.33.52-PM.png",
52 | "http://www.picture-newsletter.com/arctic/arctic-12.jpg" ]
53 | var images = [UIImage] ()
54 | var ip: [ImageProvider] = []
55 | PlaygroundPage.current.liveView = view
56 |
57 | //: ### Формируем массив 'ip : [ImageProvider]' провайдеров для загрузки и фильтрации 4-х изображений
58 |
59 | for i in 0...3 {
60 | ip.append(ImageProvider(imageURLString: imageURLs[i]) { (image) in
61 | OperationQueue.main.addOperation {view.ivs[i].image = image; print (i)}
62 | })
63 |
64 | }
65 | //: ### Стартуем загрузку изображений
66 | startClock()
67 | for ipx in ip {
68 | ipx.start()
69 | }
70 |
71 | //: ### Удаляем операции загрузки изображений спустя некоторое время
72 | sleep (6)
73 | for ipx in ip {
74 | ipx.cancel()
75 | }
76 | stopClock()
77 |
78 | //: ### Ждем окончания загрузки
79 | /*for ipx in ip {
80 | ipx.wait()
81 | }
82 | stopClock()
83 | */
84 | //PlaygroundPage.current.finishExecution()
85 | // Остановите Playground вручную, но изображение исчезнет
86 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/Sources/FourImages.swift:
--------------------------------------------------------------------------------
1 |
2 | // FourImages.swift
3 | //
4 | //
5 |
6 | import UIKit
7 | public class FourImages: UIView{
8 | public var ivs = [UIImageView] ()
9 |
10 | public override init (frame: CGRect) {
11 | super.init (frame: frame)
12 | ivs.append(UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200)))
13 | ivs.append(UIImageView(frame: CGRect(x: 200, y: 0, width: 200, height: 200)))
14 | ivs.append(UIImageView(frame: CGRect(x: 0, y: 200, width: 200, height: 200)))
15 | ivs.append(UIImageView(frame: CGRect(x: 200, y: 200, width: 200, height: 200)))
16 | for i in 0...3 {
17 | // ivs[i].contentMode = .scaleAspectFit
18 | self.addSubview(ivs[i])
19 | }
20 |
21 | }
22 |
23 | public required init?(coder aDecoder: NSCoder) {
24 | super.init(coder: aDecoder)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/Sources/GradientGenerator.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | public func topAndBottomGradient(_ size: CGSize, clearLocations: [CGFloat] = [0.4, 0.6], innerIntensity: CGFloat = 0.5) -> UIImage {
4 |
5 | let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: 0, space: CGColorSpaceCreateDeviceGray(), bitmapInfo: CGImageAlphaInfo.none.rawValue)
6 |
7 | let colors = [
8 | UIColor.white,
9 | UIColor(white: innerIntensity, alpha: 1.0),
10 | UIColor.black,
11 | UIColor(white: innerIntensity, alpha: 1.0),
12 | UIColor.white
13 | ].map { $0.cgColor }
14 | let colorLocations : [CGFloat] = [0, clearLocations[0], (clearLocations[0] + clearLocations[1]) / 2.0, clearLocations[1], 1]
15 |
16 | let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceGray(), colors: colors as CFArray, locations: colorLocations)
17 |
18 | let startPoint = CGPoint(x: 0, y: 0)
19 | let endPoint = CGPoint(x: 0, y: size.height)
20 | context?.drawLinearGradient(gradient!, start: startPoint, end: endPoint, options: CGGradientDrawingOptions())
21 |
22 | let cgImage = context?.makeImage()
23 |
24 | return UIImage(cgImage: cgImage!)
25 | }
26 |
27 |
28 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/Sources/ImageProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageProvider.swift
3 | // ConcurrencyDemo
4 | //
5 | // Created by Tatiana Kornilova on 2/8/17.
6 | // Copyright © 2017 Hossam Ghareeb. All rights reserved.
7 | //
8 | /*import UIKit
9 |
10 | public class ImageProvider {
11 | private var operationQueue = OperationQueue ()
12 |
13 | public init(imageURLString: String,completion: @escaping (UIImage?) -> ()) {
14 | operationQueue.isSuspended = true
15 | operationQueue.maxConcurrentOperationCount = 2
16 |
17 | if let imageURL = URL(string: imageURLString){
18 |
19 | // Создаем operations
20 | let dataLoad = ImageLoadOperation(url: imageURL)
21 | let filter = Filter(image: nil)
22 | let output = ImageOutputOperation(){image in
23 | OperationQueue.main.addOperation {completion (image) }}
24 |
25 | let operations = [dataLoad, filter, output]
26 |
27 | // Добавляем dependencies
28 | filter.addDependency(dataLoad)
29 | output.addDependency(filter)
30 |
31 | operationQueue.addOperations(operations, waitUntilFinished: false)
32 | }
33 | }
34 | public func start() {
35 | operationQueue.isSuspended = false
36 | }
37 |
38 | public func cancel() {
39 | operationQueue.cancelAllOperations()
40 | }
41 | public func wait() {
42 | operationQueue.waitUntilAllOperationsAreFinished()
43 | }
44 | }
45 | */
46 |
47 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/Sources/Operations/Abstract/AsyncOperation.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | open class AsyncOperation: Operation {
4 | public enum State: String {
5 | case ready, executing, finished
6 |
7 | fileprivate var keyPath: String {
8 | return "is" + rawValue.capitalized
9 | }
10 | }
11 |
12 | public var state = State.ready {
13 | willSet {
14 | willChangeValue(forKey: newValue.keyPath)
15 | willChangeValue(forKey: state.keyPath)
16 | }
17 | didSet {
18 | didChangeValue(forKey: oldValue.keyPath)
19 | didChangeValue(forKey: state.keyPath)
20 | }
21 | }
22 | }
23 |
24 |
25 | extension AsyncOperation {
26 | // NSOperation Overrides
27 | override open var isReady: Bool {
28 | return super.isReady && state == .ready
29 | }
30 |
31 | override open var isExecuting: Bool {
32 | return state == .executing
33 | }
34 |
35 | override open var isFinished: Bool {
36 | return state == .finished
37 | }
38 |
39 | override open var isAsynchronous: Bool {
40 | return true
41 | }
42 |
43 | override open func start() {
44 | if isCancelled {
45 | state = .finished
46 | return
47 | }
48 |
49 | main()
50 | state = .executing
51 | }
52 |
53 | open override func cancel() {
54 | state = .finished
55 | }
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/Sources/Operations/Abstract/ImageTakeOperation.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | protocol ImagePass {
4 | var image: UIImage? { get }
5 | }
6 |
7 | open class ImageTakeOperation: Operation {
8 | var outputImage: UIImage?
9 | private let _inputImage: UIImage?
10 |
11 | public init(image: UIImage?) {
12 | _inputImage = image
13 | super.init()
14 | }
15 |
16 | public var inputImage: UIImage? {
17 | var image: UIImage?
18 | if let inputImage = _inputImage {
19 | image = inputImage
20 | } else if let dataProvider = dependencies
21 | .filter({ $0 is ImagePass })
22 | .first as? ImagePass {
23 | image = dataProvider.image
24 | }
25 | return image
26 | }
27 | }
28 |
29 | extension ImageTakeOperation: ImagePass {
30 | var image: UIImage? {
31 | return outputImage
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/Sources/Operations/Filter.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | public class Filter : ImageTakeOperation {
4 |
5 | override public func main() {
6 | if isCancelled { return }
7 | guard let inputImage = inputImage else { return }
8 |
9 | if isCancelled { return }
10 | let mask = topAndBottomGradient(inputImage.size)
11 |
12 | if isCancelled { return }
13 | outputImage = inputImage.applyBlurWithRadius(6, maskImage: mask)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/Sources/Operations/ImageLoadOperation.swift:
--------------------------------------------------------------------------------
1 |
2 | import UIKit
3 |
4 | public class ImageLoadOperation: AsyncOperation {
5 | private var url: URL?
6 | fileprivate var outputImage: UIImage?
7 |
8 | public init(url: URL?) {
9 | self.url = url
10 | super.init()
11 | }
12 |
13 | override public func main() {
14 | if self.isCancelled { return}
15 | guard let imageURL = url else {return}
16 | let task = URLSession.shared.dataTask(with: imageURL){(data, response, error) in
17 | if self.isCancelled { return}
18 | if let data = data,
19 | let imageData = UIImage(data: data) {
20 | if self.isCancelled { return }
21 | self.outputImage = imageData
22 | }
23 | self.state = .finished
24 | }
25 | task.resume()
26 | }
27 | }
28 |
29 |
30 | extension ImageLoadOperation: ImagePass {
31 | var image: UIImage? {
32 | return outputImage
33 | }
34 | }
35 |
36 |
37 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/Sources/Operations/ImageOutputOperation.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | public class ImageOutputOperation: ImageTakeOperation {
4 |
5 | private let completion: (UIImage?) -> ()
6 |
7 | public init(completion: @escaping (UIImage?) -> ()) {
8 | self.completion = completion
9 | super.init(image: nil)
10 | }
11 |
12 | override public func main() {
13 | if isCancelled { return }
14 | completion(inputImage)
15 | }
16 | }
17 |
18 |
19 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/Sources/Operations/PostProcessingImage.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import Foundation
24 |
25 | public class PostProcessImageOperation: ImageTakeOperation {
26 |
27 | override public func main() {
28 | guard let inputImage = inputImage else { return }
29 |
30 | outputImage = postProcessImage(inputImage)
31 |
32 | }
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/Sources/PostProcess.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import CoreImage
24 | import UIKit
25 |
26 |
27 | // This is an implementation of a Core Image filter chain that returns a CGImage
28 | // backed UIImage. This is not usually the best approach - CIImage-backed
29 | // UIImages are more optimised to display straight on screen.
30 | func postProcessImage(_ image: UIImage) -> UIImage {
31 |
32 | guard let inputImage = CIImage(image: image) else { return image }
33 |
34 | // Create filter chain
35 | guard let photoFilter = CIFilter(name: "CIPhotoEffectInstant",
36 | withInputParameters: ["inputImage" : inputImage]),
37 | let photoOutput = photoFilter.outputImage,
38 | let vignetteFilter = CIFilter(name: "CIVignette",
39 | withInputParameters: ["inputRadius" : 1.75, "inputIntensity" : 1.0, "inputImage": photoOutput]),
40 | let filterOutput = vignetteFilter.outputImage else { return image }
41 |
42 | let ciContext = CIContext(options: nil)
43 |
44 | let cgImage = ciContext.createCGImage(filterOutput, from: inputImage.extent)
45 | return UIImage(cgImage: cgImage!)
46 | }
47 |
48 |
49 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/Sources/StopClock.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | fileprivate var lastStartTime = Date()
4 |
5 | public func startClock(){
6 | lastStartTime = Date()
7 | }
8 |
9 | public func stopClock() -> TimeInterval {
10 | return Date().timeIntervalSince(lastStartTime)
11 | }
12 |
13 |
14 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/Sources/UIImageExtensions.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Accelerate
3 |
4 | extension UIImage {
5 | public func applyBlurWithRadius(_ blurRadius: CGFloat, maskImage: UIImage? = nil) -> UIImage? {
6 | // Check pre-conditions.
7 | if (size.width < 1 || size.height < 1) {
8 | print("*** error: invalid size: \(size.width) x \(size.height). Both dimensions must be >= 1: \(self)")
9 | return nil
10 | }
11 | if self.cgImage == nil {
12 | print("*** error: image must be backed by a CGImage: \(self)")
13 | return nil
14 | }
15 | if maskImage != nil && maskImage!.cgImage == nil {
16 | print("*** error: maskImage must be backed by a CGImage: \(String(describing: maskImage))")
17 | return nil
18 | }
19 |
20 | let __FLT_EPSILON__ = CGFloat( Float.ulpOfOne)
21 | let screenScale = UIScreen.main.scale
22 | let imageRect = CGRect(origin: CGPoint.zero, size: size)
23 | var effectImage = self
24 |
25 | let hasBlur = blurRadius > __FLT_EPSILON__
26 |
27 | if hasBlur {
28 | func createEffectBuffer(_ context: CGContext) -> vImage_Buffer {
29 | let data = context.data
30 | let width = vImagePixelCount(context.width)
31 | let height = vImagePixelCount(context.height)
32 | let rowBytes = context.bytesPerRow
33 |
34 | return vImage_Buffer(data: data, height: height, width: width, rowBytes: rowBytes)
35 | }
36 |
37 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
38 | let effectInContext = UIGraphicsGetCurrentContext()!
39 |
40 | effectInContext.scaleBy(x: 1.0, y: -1.0)
41 | effectInContext.translateBy(x: 0, y: -size.height)
42 | effectInContext.draw(self.cgImage!, in: imageRect)
43 |
44 | var effectInBuffer = createEffectBuffer(effectInContext)
45 |
46 |
47 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
48 | let effectOutContext = UIGraphicsGetCurrentContext()!
49 |
50 | var effectOutBuffer = createEffectBuffer(effectOutContext)
51 |
52 |
53 | if hasBlur {
54 | // A description of how to compute the box kernel width from the Gaussian
55 | // radius (aka standard deviation) appears in the SVG spec:
56 | // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
57 | //
58 | // For larger values of 's' (s >= 2.0), an approximation can be used: Three
59 | // successive box-blurs build a piece-wise quadratic convolution kernel, which
60 | // approximates the Gaussian kernel to within roughly 3%.
61 | //
62 | // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
63 | //
64 | // ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
65 | //
66 |
67 | let inputRadius = blurRadius * screenScale
68 | var radius = UInt32(floor(Double(inputRadius * 0.75 * sqrt(2.0 * .pi) + 0.5)))
69 | if radius % 2 != 1 {
70 | radius += 1 // force radius to be odd so that the three box-blur methodology works.
71 | }
72 |
73 | let imageEdgeExtendFlags = vImage_Flags(kvImageEdgeExtend)
74 |
75 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
76 | vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
77 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
78 | }
79 |
80 | effectImage = UIGraphicsGetImageFromCurrentImageContext()!
81 |
82 | UIGraphicsEndImageContext()
83 | UIGraphicsEndImageContext()
84 | }
85 |
86 | // Set up output context.
87 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
88 | let outputContext = UIGraphicsGetCurrentContext()
89 | outputContext?.scaleBy(x: 1.0, y: -1.0)
90 | outputContext?.translateBy(x: 0, y: -size.height)
91 |
92 | // Draw base image.
93 | outputContext?.draw(self.cgImage!, in: imageRect)
94 |
95 | // Draw effect image.
96 | if hasBlur {
97 | outputContext?.saveGState()
98 | if let image = maskImage {
99 | //CGContextClipToMask(outputContext, imageRect, image.CGImage);
100 | let effectCGImage = effectImage.cgImage?.masking(image.cgImage!)
101 | if let effectCGImage = effectCGImage {
102 | effectImage = UIImage(cgImage: effectCGImage)
103 | }
104 | }
105 | outputContext?.draw(effectImage.cgImage!, in: imageRect)
106 | outputContext?.restoreGState()
107 | }
108 |
109 | // Output image is ready.
110 | let outputImage = UIGraphicsGetImageFromCurrentImageContext()
111 | UIGraphicsEndImageContext()
112 |
113 | return outputImage
114 | }
115 | }
116 |
117 |
118 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CancellationFourImages.playground/playground.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/CancellationFourImages.playground/playground.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/CancellationFourImages.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/CancellationGroup.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | //: # Operation Cancellation
3 | //: Очень важно убедиться, что индивидуальные операции реагируют на свойство `isCancelled` и, следовательно, могут быть уничтожены.
4 | //:
5 | //: Тем не менее в дополнение к уничтожению отдельных операциям вы можете уничтожить все операции на определенной очереди операций. Это особенно полезно, если у вас есть набор операций, работающих на единую цель, при этом эта единая цель разделена на независимые операции, которые запускаются параллельно, или представляет собой граф зависимых операций, исполняемых одна за другой.
6 | //:
7 | //: В этой реализации мы достигнем того же самого, что и операция `ArraySumOperation`, которая берет массив кортежей (Int, Int), и, используя функцию медленного сложения slowAdd(), создает массив сумм чисел, составляющих кортеж. Но в нашем случае будет множество отдельных операций `SumOperation`:
8 | class SumOperation: Operation {
9 | let inputPair: (Int, Int)
10 | var output: Int?
11 |
12 | init(input: (Int, Int)) {
13 | inputPair = input
14 | super.init()
15 | }
16 |
17 | override func main() {
18 | if isCancelled { return }
19 | output = slowAdd(inputPair)
20 | }
21 | }
22 | //: Класс `GroupAdd` - это обычный класс, который управляет очередью операций и множеством операций `SumOperation` для того, чтобы посчитать сумму всех пар во входном массиве.
23 | class GroupAdd {
24 | private let queue = OperationQueue()
25 | private let appendQueue = OperationQueue()
26 | var outputArray = [(Int, Int, Int)]()
27 |
28 | init(input: [(Int, Int)]) {
29 | queue.isSuspended = true
30 | queue.maxConcurrentOperationCount = 2
31 | appendQueue.maxConcurrentOperationCount = 1
32 | generateOperations(input)
33 | }
34 |
35 | private func generateOperations(_ numberArray: [(Int, Int)]) {
36 | for pair in numberArray {
37 | let operation = SumOperation(input: pair)
38 | operation.completionBlock = {
39 |
40 | // Заметьте: добавляем в массив на очереди операций appendQueue.
41 | guard let result = operation.output else { return }
42 | self.appendQueue.addOperation {
43 | self.outputArray.append((pair.0, pair.1, result))
44 | }
45 | }
46 | queue.addOperation(operation)
47 | }
48 | }
49 |
50 | func start() {
51 | queue.isSuspended = false
52 | }
53 |
54 | func cancel() {
55 | queue.cancelAllOperations()
56 | }
57 |
58 | func wait() {
59 | queue.waitUntilAllOperationsAreFinished()
60 | }
61 | }
62 | //: Входной массив
63 | let numberArray = [(1,2), (3,4), (5,6), (7,8), (9,10)]
64 | //: Создаем экземпляр класса `GroupAdd`
65 | let groupAdd = GroupAdd(input: numberArray)
66 | //: Стартуем группу
67 | startClock()
68 | groupAdd.start()
69 | //: __TEST:__ Попробуйте снять комментарий с `cancel()`, чтобы проверить, сработает ли этот метод корректно. После завершения операций останавливаем таймер `stopClock()` и увидим другую длину выходного массива.
70 | sleep(1)
71 | groupAdd.cancel()
72 |
73 | groupAdd.wait()
74 | stopClock()
75 | //: Проверяем результат
76 | groupAdd.outputArray
77 |
--------------------------------------------------------------------------------
/CancellationGroup.playground/Sources/DelayUtil.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import Foundation
24 |
25 | public func delay(_ delay: Double, closure: @escaping () -> ()) {
26 | DispatchQueue.main.asyncAfter(
27 | deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC),
28 | execute: closure)
29 | }
30 |
--------------------------------------------------------------------------------
/CancellationGroup.playground/Sources/SlowSum.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import Foundation
24 |
25 | public func slowAdd(_ input: (Int, Int)) -> Int {
26 | sleep(1)
27 | return input.0 + input.1
28 | }
29 |
30 | public func slowAddArray(input: [(Int, Int)], progress: ((Double) -> (Bool))? = nil) -> [Int] {
31 | var results = [Int]()
32 | for pair in input {
33 | results.append(slowAdd(pair))
34 | if let progress = progress {
35 | if !progress(Double(results.count) / Double(input.count)) { return results }
36 | }
37 | }
38 | return results
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/CancellationGroup.playground/Sources/StopClock.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import Foundation
24 |
25 | fileprivate var lastStartTime = Date()
26 |
27 | public func startClock(){
28 | lastStartTime = Date()
29 | }
30 |
31 | public func stopClock() -> TimeInterval {
32 | return Date().timeIntervalSince(lastStartTime)
33 | }
34 |
--------------------------------------------------------------------------------
/CancellationGroup.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/CancellationGroup.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CancellationGroup.playground/playground.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/CancellationGroup.playground/playground.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/CancellationGroup.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/LoadAndFilter.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import PlaygroundSupport
3 | PlaygroundPage.current.needsIndefiniteExecution = true
4 | //: ## Dependencies
5 | //: Операция загрузки `ImageLoadOperation` уже нам знакома:
6 | class ImageLoadOperation: AsyncOperation {
7 | var urlString: String?
8 | var outputImage: UIImage?
9 |
10 | init(urlString: String?) {
11 | self.urlString = urlString
12 | super.init()
13 | }
14 | override func main() {
15 | asyncImageLoad (urlString: self.urlString) { [unowned self] (image) in
16 | self.outputImage = image
17 | self.state = .finished
18 | }
19 | }
20 | }
21 |
22 | //: Передача данных в зависимую операцию c помощью протокола
23 | protocol ImagePass {
24 | var image: UIImage? { get }
25 | }
26 |
27 | extension ImageLoadOperation: ImagePass {
28 | var image: UIImage? { return outputImage }
29 | }
30 | //: Операция фильтрации `FilterOperation`:
31 | class FilterOperation: Operation {
32 | var outputImage: UIImage?
33 | private let _inputImage: UIImage?
34 |
35 | init(_ image: UIImage?) {
36 | _inputImage = image
37 | super.init()
38 | }
39 |
40 | var inputImage: UIImage? {
41 | // Определяем, задан ли у операции inputImage
42 | // Если НЕТ, то анализируем dependencies,
43 | // которые "подтверждают" протокол ImagePass
44 | var image: UIImage?
45 | if let inputImage = _inputImage {
46 | image = inputImage
47 | } else if let dataProvider = dependencies
48 | .filter({ $0 is ImagePass })
49 | .first as? ImagePass {
50 | image = dataProvider.image
51 | }
52 | return image
53 | }
54 |
55 | override func main() {
56 | outputImage = filterImage(image: inputImage)
57 | }
58 | }
59 | //: Определяем две операции:
60 | let urlString = "http://www.planetware.com/photos-large/F/france-paris-eiffel-tower.jpg"
61 | let imageLoad = ImageLoadOperation(urlString:urlString )
62 | let filter = FilterOperation(nil)
63 | //: Устанавливаем зависимость между операциями imageLoad и filter:
64 | filter.addDependency(imageLoad)
65 | //: Добавляем обе операции в очередь операций:
66 | let queue = OperationQueue()
67 | queue.addOperations([imageLoad, filter], waitUntilFinished: true)
68 | //: И смотрите, что у нас на выходе:
69 | imageLoad.outputImage
70 | filter.outputImage
71 |
72 | sleep(3)
73 | //PlaygroundPage.current.finishExecution()
74 |
--------------------------------------------------------------------------------
/LoadAndFilter.playground/Resources/dark_road_small.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/LoadAndFilter.playground/Resources/dark_road_small.jpg
--------------------------------------------------------------------------------
/LoadAndFilter.playground/Resources/train_day.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/LoadAndFilter.playground/Resources/train_day.jpg
--------------------------------------------------------------------------------
/LoadAndFilter.playground/Resources/train_dusk.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/LoadAndFilter.playground/Resources/train_dusk.jpg
--------------------------------------------------------------------------------
/LoadAndFilter.playground/Resources/train_night.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/LoadAndFilter.playground/Resources/train_night.jpg
--------------------------------------------------------------------------------
/LoadAndFilter.playground/Sources/AsyncOperation.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 |
24 | import Foundation
25 |
26 | open class AsyncOperation: Operation {
27 | public enum State: String {
28 | case ready, executing, finished
29 |
30 | fileprivate var keyPath: String {
31 | return "is" + rawValue.capitalized
32 | }
33 | }
34 |
35 | public var state = State.ready {
36 | willSet {
37 | willChangeValue(forKey: newValue.keyPath)
38 | willChangeValue(forKey: state.keyPath)
39 | }
40 | didSet {
41 | didChangeValue(forKey: oldValue.keyPath)
42 | didChangeValue(forKey: state.keyPath)
43 | }
44 | }
45 | }
46 |
47 |
48 | extension AsyncOperation {
49 | // NSOperation Overrides
50 | override open var isReady: Bool {
51 | return super.isReady && state == .ready
52 | }
53 |
54 | override open var isExecuting: Bool {
55 | return state == .executing
56 | }
57 |
58 | override open var isFinished: Bool {
59 | return state == .finished
60 | }
61 |
62 | override open var isAsynchronous: Bool {
63 | return true
64 | }
65 |
66 | override open func start() {
67 | if isCancelled {
68 | state = .finished
69 | return
70 | }
71 |
72 | main()
73 | state = .executing
74 | }
75 |
76 | open override func cancel() {
77 | state = .finished
78 | }
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/LoadAndFilter.playground/Sources/Duration.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import Foundation
24 |
25 | public func duration(_ block: () -> ()) -> TimeInterval {
26 | let startTime = Date()
27 | block()
28 | return Date().timeIntervalSince(startTime)
29 | }
30 |
--------------------------------------------------------------------------------
/LoadAndFilter.playground/Sources/Filter.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | public func filterImage(image: UIImage?) -> UIImage? {
4 | guard let image = image else { return .none }
5 | sleep(1)
6 | let mask = topAndBottomGradient(size: image.size)
7 | return image.applyBlur(radius: 6, maskImage: mask)
8 | }
9 |
10 | func filterAsync(image: UIImage?, callback: @escaping (UIImage?) ->()) {
11 | OperationQueue().addOperation {
12 | let result = filterImage(image: image)
13 | callback(result)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/LoadAndFilter.playground/Sources/GradientGenerator.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import UIKit
24 |
25 | func topAndBottomGradient(size: CGSize, clearLocations: [CGFloat] = [0.35, 0.65], innerIntensity: CGFloat = 0.5) -> UIImage {
26 |
27 | let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: 0, space: CGColorSpaceCreateDeviceGray(), bitmapInfo: CGImageAlphaInfo.none.rawValue)
28 |
29 | let colors = [
30 | .white,
31 | UIColor(white: innerIntensity, alpha: 1.0),
32 | .black,
33 | UIColor(white: innerIntensity, alpha: 1.0),
34 | .white
35 | ].map { $0.cgColor }
36 | let colorLocations : [CGFloat] = [0, clearLocations[0], (clearLocations[0] + clearLocations[1]) / 2.0, clearLocations[1], 1]
37 |
38 | let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceGray(), colors: colors as CFArray, locations: colorLocations)
39 |
40 | let startPoint = CGPoint(x: 0, y: 0)
41 | let endPoint = CGPoint(x: 0, y: size.height)
42 |
43 | context?.drawLinearGradient(gradient!, start: startPoint, end: endPoint, options: CGGradientDrawingOptions())
44 | let cgImage = context!.makeImage()
45 |
46 | return UIImage(cgImage: cgImage!)
47 |
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/LoadAndFilter.playground/Sources/ImageLoad.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | public func asyncImageLoad(urlString: String?,callback: @escaping (UIImage?) -> ()) {
4 | guard let urlString = urlString,
5 | let imageURL = URL(string: urlString) else { return callback (nil) }
6 |
7 | let task = URLSession.shared.dataTask(with: imageURL){
8 | (data, response, error) in
9 | if let data = data {
10 | callback (UIImage(data: data))
11 | }
12 | }
13 | task.resume()
14 | }
15 |
--------------------------------------------------------------------------------
/LoadAndFilter.playground/Sources/UIImage+Blur.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import UIKit
24 | import Accelerate
25 |
26 | extension UIImage {
27 | public func applyBlur(radius: CGFloat, maskImage: UIImage? = nil) -> UIImage? {
28 | // Check pre-conditions.
29 | if (size.width < 1 || size.height < 1) {
30 | print("*** error: invalid size: \(size.width) x \(size.height). Both dimensions must be >= 1: \(self)")
31 | return nil
32 | }
33 | if self.cgImage == nil {
34 | print("*** error: image must be backed by a CGImage: \(self)")
35 | return nil
36 | }
37 | if maskImage != nil && maskImage!.cgImage == nil {
38 | print("*** error: maskImage must be backed by a CGImage: \(String(describing: maskImage))")
39 | return nil
40 | }
41 |
42 | let __FLT_EPSILON__ = CGFloat(Float.ulpOfOne)
43 | let screenScale = UIScreen.main.scale
44 | let imageRect = CGRect(origin: .zero, size: size)
45 | var effectImage = self
46 |
47 | let hasBlur = radius > __FLT_EPSILON__
48 |
49 | if hasBlur {
50 | func createEffectBuffer(context: CGContext) -> vImage_Buffer {
51 | let data = context.data
52 | let width = vImagePixelCount(context.width)
53 | let height = vImagePixelCount(context.height)
54 | let rowBytes = context.bytesPerRow
55 |
56 | return vImage_Buffer(data: data, height: height, width: width, rowBytes: rowBytes)
57 | }
58 |
59 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
60 | let effectInContext = UIGraphicsGetCurrentContext()!
61 |
62 | effectInContext.scaleBy(x: 1.0, y: -1.0)
63 | effectInContext.translateBy(x: 0, y: -size.height)
64 | effectInContext.draw(self.cgImage!, in: imageRect)
65 |
66 | var effectInBuffer = createEffectBuffer(context: effectInContext)
67 |
68 |
69 |
70 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
71 | let effectOutContext = UIGraphicsGetCurrentContext()!
72 |
73 | var effectOutBuffer = createEffectBuffer(context: effectOutContext)
74 |
75 |
76 | if hasBlur {
77 | // A description of how to compute the box kernel width from the Gaussian
78 | // radius (aka standard deviation) appears in the SVG spec:
79 | // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
80 | //
81 | // For larger values of 's' (s >= 2.0), an approximation can be used: Three
82 | // successive box-blurs build a piece-wise quadratic convolution kernel, which
83 | // approximates the Gaussian kernel to within roughly 3%.
84 | //
85 | // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
86 | //
87 | // ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
88 | //
89 |
90 | let inputRadius = radius * screenScale
91 | var radius = UInt32(floor(Double(inputRadius * 0.75 * sqrt(2.0 * .pi) + 0.5)))
92 | if radius % 2 != 1 {
93 | radius += 1 // force radius to be odd so that the three box-blur methodology works.
94 | }
95 |
96 | let imageEdgeExtendFlags = vImage_Flags(kvImageEdgeExtend)
97 |
98 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
99 | vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
100 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
101 | }
102 |
103 | effectImage = UIGraphicsGetImageFromCurrentImageContext()!
104 |
105 | UIGraphicsEndImageContext()
106 | UIGraphicsEndImageContext()
107 | }
108 |
109 | // Set up output context.
110 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
111 | let outputContext = UIGraphicsGetCurrentContext()
112 | outputContext!.scaleBy(x: 1.0, y: -1.0)
113 | outputContext!.translateBy(x: 0, y: -size.height)
114 |
115 | // Draw base image.
116 | outputContext!.draw(self.cgImage!, in: imageRect)
117 |
118 | // Draw effect image.
119 | if hasBlur {
120 | outputContext!.saveGState()
121 | if let image = maskImage {
122 | let effectCGImage = effectImage.cgImage!.masking(image.cgImage!)
123 | if let effectCGImage = effectCGImage {
124 | effectImage = UIImage(cgImage: effectCGImage)
125 | }
126 | }
127 | outputContext!.draw(effectImage.cgImage!, in: imageRect)
128 | outputContext!.restoreGState()
129 | }
130 |
131 | // Output image is ready.
132 | let outputImage = UIGraphicsGetImageFromCurrentImageContext()
133 | UIGraphicsEndImageContext()
134 |
135 | return outputImage
136 | }
137 | }
138 |
139 |
--------------------------------------------------------------------------------
/LoadAndFilter.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/LoadAndFilter.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LoadAndFilter.playground/playground.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/LoadAndFilter.playground/playground.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/LoadAndFilter.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
14 |
15 |
19 |
20 |
24 |
25 |
29 |
30 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/Operation.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import PlaygroundSupport
3 | PlaygroundPage.current.needsIndefiniteExecution = true
4 | //: # Operation
5 | //: `Operation` представляет собой 'задачу'
6 | //: ## простейшая `Operation`
7 | //: используется только совместно c `OperationQueue` с помощью метода `addOperation`
8 | let operation1 = {
9 | print ("Operation 1 started")
10 | print ("Operation 1 finished")
11 | }
12 |
13 | let queue = OperationQueue()
14 | queue.addOperation(operation1)
15 | //: Полноценная `Operation` может быть сконструирована как `BlockOperation` или как пользовательский subclass `Operation`.
16 | //: ## BlockOperation
17 | //: Создаем `BlockOperation` для конкатенации двух строк
18 | var result: String?
19 |
20 | let concatenationOperation = BlockOperation {
21 | result = "💧" + "☂️"
22 | sleep(2)
23 | }
24 | duration {
25 | concatenationOperation.start()
26 | }
27 | result
28 |
29 | //: Создаем `BlockOperation` с можеством блоков:
30 | let multiPrinter = BlockOperation()
31 | multiPrinter.completionBlock = {
32 | print("Finished multiPrinting!")
33 | }
34 |
35 | multiPrinter.addExecutionBlock { print("Каждый 🍎"); sleep(2) }
36 | multiPrinter.addExecutionBlock { print("Охотник 🍊"); sleep(2) }
37 | multiPrinter.addExecutionBlock { print("Желает 🍋"); sleep(2) }
38 | multiPrinter.addExecutionBlock { print("Знать 🍏"); sleep(2) }
39 | multiPrinter.addExecutionBlock { print("Где 💎"); sleep(2) }
40 | multiPrinter.addExecutionBlock { print("Сидит 🚙"); sleep(2) }
41 | multiPrinter.addExecutionBlock { print("Фазан 🍆"); sleep(2) }
42 |
43 | duration {
44 | multiPrinter.start()
45 | }
46 | //: ## Subclassing `Operation`
47 | //: Позволяет вам осуществлять большее управление над тем, что делает `Operation`
48 | let inputImage = UIImage(named: "dark_road_small.jpg")
49 | // DONE: Создайте и запустите FilterOperation
50 |
51 | class FilterOperation: Operation {
52 | var inputImage: UIImage?
53 | var outputImage: UIImage?
54 |
55 | override func main() {
56 | outputImage = filter(image: inputImage)
57 | }
58 | }
59 |
60 | let filterOp = FilterOperation()
61 | filterOp.inputImage = inputImage
62 |
63 | duration {
64 | filterOp.start()
65 | }
66 | filterOp.outputImage
67 |
68 | PlaygroundPage.current.finishExecution()
69 |
--------------------------------------------------------------------------------
/Operation.playground/Resources/dark_road_small.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/Operation.playground/Resources/dark_road_small.jpg
--------------------------------------------------------------------------------
/Operation.playground/Sources/Duration.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import Foundation
24 |
25 | public func duration(_ block: () -> ()) -> TimeInterval {
26 | let startTime = Date()
27 | block()
28 | return Date().timeIntervalSince(startTime)
29 | }
30 |
--------------------------------------------------------------------------------
/Operation.playground/Sources/Filter.swift:
--------------------------------------------------------------------------------
1 |
2 | import UIKit
3 |
4 | public func filter(image: UIImage?) -> UIImage? {
5 | guard let image = image else { return .none }
6 | sleep(1)
7 | let mask = topAndBottomGradient(size: image.size)
8 | return image.applyBlur(radius: 6, maskImage: mask)
9 | }
10 |
11 | func filterAsync(image: UIImage?, callback: @escaping (UIImage?) ->()) {
12 | OperationQueue().addOperation {
13 | let result = filter(image: image)
14 | callback(result)
15 | }
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/Operation.playground/Sources/GradientGenerator.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import UIKit
24 |
25 | func topAndBottomGradient(size: CGSize, clearLocations: [CGFloat] = [0.35, 0.65], innerIntensity: CGFloat = 0.5) -> UIImage {
26 |
27 | let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: 0, space: CGColorSpaceCreateDeviceGray(), bitmapInfo: CGImageAlphaInfo.none.rawValue)
28 |
29 | let colors = [
30 | .white,
31 | UIColor(white: innerIntensity, alpha: 1.0),
32 | .black,
33 | UIColor(white: innerIntensity, alpha: 1.0),
34 | .white
35 | ].map { $0.cgColor }
36 | let colorLocations : [CGFloat] = [0, clearLocations[0], (clearLocations[0] + clearLocations[1]) / 2.0, clearLocations[1], 1]
37 |
38 | let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceGray(), colors: colors as CFArray, locations: colorLocations)
39 |
40 | let startPoint = CGPoint(x: 0, y: 0)
41 | let endPoint = CGPoint(x: 0, y: size.height)
42 |
43 | context?.drawLinearGradient(gradient!, start: startPoint, end: endPoint, options: CGGradientDrawingOptions())
44 | let cgImage = context!.makeImage()
45 |
46 | return UIImage(cgImage: cgImage!)
47 |
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/Operation.playground/Sources/UIImageExtensions.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import UIKit
24 | import Accelerate
25 |
26 | extension UIImage {
27 | public func applyBlur(radius: CGFloat, maskImage: UIImage? = nil) -> UIImage? {
28 | // Check pre-conditions.
29 | if (size.width < 1 || size.height < 1) {
30 | print("*** error: invalid size: \(size.width) x \(size.height). Both dimensions must be >= 1: \(self)")
31 | return nil
32 | }
33 | if self.cgImage == nil {
34 | print("*** error: image must be backed by a CGImage: \(self)")
35 | return nil
36 | }
37 | if maskImage != nil && maskImage!.cgImage == nil {
38 | print("*** error: maskImage must be backed by a CGImage: \(String(describing: maskImage))")
39 | return nil
40 | }
41 |
42 | let __FLT_EPSILON__ = CGFloat( Float.ulpOfOne)
43 | let screenScale = UIScreen.main.scale
44 | let imageRect = CGRect(origin: .zero, size: size)
45 | var effectImage = self
46 |
47 | let hasBlur = radius > __FLT_EPSILON__
48 |
49 | if hasBlur {
50 | func createEffectBuffer(context: CGContext) -> vImage_Buffer {
51 | let data = context.data
52 | let width = vImagePixelCount(context.width)
53 | let height = vImagePixelCount(context.height)
54 | let rowBytes = context.bytesPerRow
55 |
56 | return vImage_Buffer(data: data, height: height, width: width, rowBytes: rowBytes)
57 | }
58 |
59 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
60 | let effectInContext = UIGraphicsGetCurrentContext()!
61 |
62 | effectInContext.scaleBy(x: 1.0, y: -1.0)
63 | effectInContext.translateBy(x: 0, y: -size.height)
64 | effectInContext.draw(self.cgImage!, in: imageRect)
65 |
66 | var effectInBuffer = createEffectBuffer(context: effectInContext)
67 |
68 |
69 |
70 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
71 | let effectOutContext = UIGraphicsGetCurrentContext()!
72 |
73 | var effectOutBuffer = createEffectBuffer(context: effectOutContext)
74 |
75 |
76 | if hasBlur {
77 | // A description of how to compute the box kernel width from the Gaussian
78 | // radius (aka standard deviation) appears in the SVG spec:
79 | // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
80 | //
81 | // For larger values of 's' (s >= 2.0), an approximation can be used: Three
82 | // successive box-blurs build a piece-wise quadratic convolution kernel, which
83 | // approximates the Gaussian kernel to within roughly 3%.
84 | //
85 | // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
86 | //
87 | // ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
88 | //
89 |
90 | let inputRadius = radius * screenScale
91 | var radius = UInt32(floor(Double(inputRadius * 0.75 * sqrt(2.0 * .pi) + 0.5)))
92 | if radius % 2 != 1 {
93 | radius += 1 // force radius to be odd so that the three box-blur methodology works.
94 | }
95 |
96 | let imageEdgeExtendFlags = vImage_Flags(kvImageEdgeExtend)
97 |
98 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
99 | vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
100 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
101 | }
102 |
103 | effectImage = UIGraphicsGetImageFromCurrentImageContext()!
104 |
105 | UIGraphicsEndImageContext()
106 | UIGraphicsEndImageContext()
107 | }
108 |
109 | // Set up output context.
110 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
111 | let outputContext = UIGraphicsGetCurrentContext()
112 | outputContext!.scaleBy(x: 1.0, y: -1.0)
113 | outputContext!.translateBy(x: 0, y: -size.height)
114 |
115 | // Draw base image.
116 | outputContext!.draw(self.cgImage!, in: imageRect)
117 |
118 | // Draw effect image.
119 | if hasBlur {
120 | outputContext!.saveGState()
121 | if let image = maskImage {
122 | let effectCGImage = effectImage.cgImage!.masking(image.cgImage!)
123 | if let effectCGImage = effectCGImage {
124 | effectImage = UIImage(cgImage: effectCGImage)
125 | }
126 | }
127 | outputContext!.draw(effectImage.cgImage!, in: imageRect)
128 | outputContext!.restoreGState()
129 | }
130 |
131 | // Output image is ready.
132 | let outputImage = UIGraphicsGetImageFromCurrentImageContext()
133 | UIGraphicsEndImageContext()
134 |
135 | return outputImage
136 | }
137 | }
138 |
139 |
--------------------------------------------------------------------------------
/Operation.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Operation.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Operation.playground/playground.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/Operation.playground/playground.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Operation.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
14 |
15 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/OperationQueue.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import PlaygroundSupport
3 | PlaygroundPage.current.needsIndefiniteExecution = true
4 | //: # OperationQueue
5 | //: `OperationQueue` отвечает за планирование и запуск группы операций по умолчанию на background.
6 | //: ## Создание очереди операций
7 | //: Создание очереди операций очень просто с использованием default initializer; вы также можете установить максимальное число операций, которые будут выполняться одновременно, а также задать очередь, на которой будут выполняться операции
8 | // Создаем пустую очередь printerQueue
9 | let printerQueue = OperationQueue()
10 |
11 | //printerQueue.maxConcurrentOperationCount = 2
12 | //printerQueue.underlyingQueue = DispatchQueue(label: "...", qos: .userInitiated, attributes:.concurrent)
13 | //: ## Добавляем операции `Operations` на очередь операций
14 | /*: Операции `Operations` можно добавлять на очередь напрямую как замыкания
15 | - ВАЖНО:
16 | Добавление операций в очередь действительно "ничего не стоит"; хотя операции начинают выполняться как только окажутся в очереди, добавление их происходит полностью асинхронно.
17 | \
18 | Вы можете это обнаружить по результатам работы функции `duration` :
19 | */
20 | let concatenationOperation = BlockOperation {
21 | print ( "-------💧" + "☂️")
22 | sleep(2)
23 | }
24 | concatenationOperation.qualityOfService = .userInitiated
25 |
26 | // DONE: Добавляем 5 операций на очередь printerQueue
27 |
28 | duration {
29 |
30 | printerQueue.addOperation { print("Каждый 🍎"); sleep(2) }
31 | printerQueue.addOperation { print("Охотник 🍊");sleep(2) }
32 | printerQueue.addOperation { print("Желает 🍋"); sleep(2) }
33 | printerQueue.addOperation { print("Знать 🍏"); sleep(2) }
34 | printerQueue.addOperation { print("Где 💎"); sleep(2) }
35 | printerQueue.addOperation { print("Сидит 🚙"); sleep(2) }
36 | printerQueue.addOperation { print("Фазан 🍆"); sleep(2) }
37 |
38 | printerQueue.addOperation(concatenationOperation)
39 |
40 | }
41 |
42 | // DONE: Измеряем длительность всех операций
43 | duration {
44 | printerQueue.waitUntilAllOperationsAreFinished()
45 | }
46 | //: ## Фильтрация массива изображений
47 | //: Теперь более реальный пример.
48 | // Исходные изображения
49 | let images = ["city", "dark_road", "train_day", "train_dusk", "train_night"].map { UIImage(named: "\($0).jpg") }
50 |
51 | // Отфильтрованные изображения
52 | var filteredImages = [UIImage]()
53 | //: Создаем очередь filterQueue с помощью default initializer:
54 | let filterQueue = OperationQueue()
55 | //: Создаем последовательную очередь (serial queue) appendQueue для добавления элемента к массиву:
56 | let appendQueue = OperationQueue()
57 | appendQueue.maxConcurrentOperationCount = 1
58 | //: Создаем операцию фильтрации для каждого изображения, добавляя `completionBlock`:
59 | for image in images {
60 |
61 | let filterOp = FilterOperation()
62 | filterOp.inputImage = image
63 | filterOp.completionBlock = {
64 | guard let output = filterOp.outputImage else { return }
65 | appendQueue.addOperation {
66 | filteredImages.append(output)
67 | }
68 | }
69 | filterQueue.addOperation(filterOp)
70 | }
71 | //: Необходимо дождаться, пока закончатся операции на очереди прежде, чем проверять результаты
72 | // Ждем...
73 | duration {
74 | filterQueue.waitUntilAllOperationsAreFinished()
75 | }
76 | //: Проверяем отфильтрованные изображения
77 | filteredImages[0]
78 |
79 | filteredImages[1]
80 | filteredImages[4]
81 | //filterOp.outputImage
82 | PlaygroundPage.current.finishExecution()
83 |
--------------------------------------------------------------------------------
/OperationQueue.playground/Resources/city.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/OperationQueue.playground/Resources/city.jpg
--------------------------------------------------------------------------------
/OperationQueue.playground/Resources/dark_road.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/OperationQueue.playground/Resources/dark_road.jpg
--------------------------------------------------------------------------------
/OperationQueue.playground/Resources/train_day.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/OperationQueue.playground/Resources/train_day.jpg
--------------------------------------------------------------------------------
/OperationQueue.playground/Resources/train_dusk.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/OperationQueue.playground/Resources/train_dusk.jpg
--------------------------------------------------------------------------------
/OperationQueue.playground/Resources/train_night.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/OperationQueue.playground/Resources/train_night.jpg
--------------------------------------------------------------------------------
/OperationQueue.playground/Sources/Duration.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import Foundation
24 |
25 | public func duration(_ block: () -> ()) -> TimeInterval {
26 | let startTime = Date()
27 | block()
28 | return Date().timeIntervalSince(startTime)
29 | }
30 |
--------------------------------------------------------------------------------
/OperationQueue.playground/Sources/Filter.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 |
4 | public class FilterOperation : Operation {
5 | public var inputImage: UIImage?
6 | public var outputImage: UIImage?
7 |
8 | override public func main() {
9 | guard let inputImage = inputImage else { return }
10 | outputImage = filter(image: inputImage)
11 | }
12 | }
13 | public func filter(image: UIImage?) -> UIImage? {
14 | guard let image = image else { return .none }
15 | sleep(1)
16 | let mask = topAndBottomGradient(size: image.size)
17 | return image.applyBlur(radius: 6, maskImage: mask)
18 | }
19 |
20 | func filterAsync(image: UIImage?, callback: @escaping (UIImage?) ->()) {
21 | OperationQueue().addOperation {
22 | let result = filter(image: image)
23 | callback(result)
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/OperationQueue.playground/Sources/GradientGenerator.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import UIKit
24 |
25 | func topAndBottomGradient(size: CGSize, clearLocations: [CGFloat] = [0.35, 0.65], innerIntensity: CGFloat = 0.5) -> UIImage {
26 |
27 | let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: 0, space: CGColorSpaceCreateDeviceGray(), bitmapInfo: CGImageAlphaInfo.none.rawValue)
28 |
29 | let colors = [
30 | .white,
31 | UIColor(white: innerIntensity, alpha: 1.0),
32 | .black,
33 | UIColor(white: innerIntensity, alpha: 1.0),
34 | .white
35 | ].map { $0.cgColor }
36 | let colorLocations : [CGFloat] = [0, clearLocations[0], (clearLocations[0] + clearLocations[1]) / 2.0, clearLocations[1], 1]
37 |
38 | let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceGray(), colors: colors as CFArray, locations: colorLocations)
39 |
40 | let startPoint = CGPoint(x: 0, y: 0)
41 | let endPoint = CGPoint(x: 0, y: size.height)
42 |
43 | context?.drawLinearGradient(gradient!, start: startPoint, end: endPoint, options: CGGradientDrawingOptions())
44 | let cgImage = context!.makeImage()
45 |
46 | return UIImage(cgImage: cgImage!)
47 |
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/OperationQueue.playground/Sources/StopClock.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import Foundation
24 |
25 | fileprivate var lastStartTime = Date()
26 |
27 | public func startClock(){
28 | lastStartTime = Date()
29 | }
30 |
31 | public func stopClock() -> TimeInterval {
32 | return Date().timeIntervalSince(lastStartTime)
33 | }
34 |
--------------------------------------------------------------------------------
/OperationQueue.playground/Sources/UIImageExtensions.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import UIKit
24 | import Accelerate
25 |
26 | extension UIImage {
27 | public func applyBlur(radius: CGFloat, maskImage: UIImage? = nil) -> UIImage? {
28 | // Check pre-conditions.
29 | if (size.width < 1 || size.height < 1) {
30 | print("*** error: invalid size: \(size.width) x \(size.height). Both dimensions must be >= 1: \(self)")
31 | return nil
32 | }
33 | if self.cgImage == nil {
34 | print("*** error: image must be backed by a CGImage: \(self)")
35 | return nil
36 | }
37 | if maskImage != nil && maskImage!.cgImage == nil {
38 | print("*** error: maskImage must be backed by a CGImage: \(String(describing: maskImage))")
39 | return nil
40 | }
41 |
42 | let __FLT_EPSILON__ = CGFloat( Float.ulpOfOne)
43 | let screenScale = UIScreen.main.scale
44 | let imageRect = CGRect(origin: .zero, size: size)
45 | var effectImage = self
46 |
47 | let hasBlur = radius > __FLT_EPSILON__
48 |
49 | if hasBlur {
50 | func createEffectBuffer(context: CGContext) -> vImage_Buffer {
51 | let data = context.data
52 | let width = vImagePixelCount(context.width)
53 | let height = vImagePixelCount(context.height)
54 | let rowBytes = context.bytesPerRow
55 |
56 | return vImage_Buffer(data: data, height: height, width: width, rowBytes: rowBytes)
57 | }
58 |
59 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
60 | let effectInContext = UIGraphicsGetCurrentContext()!
61 |
62 | effectInContext.scaleBy(x: 1.0, y: -1.0)
63 | effectInContext.translateBy(x: 0, y: -size.height)
64 | effectInContext.draw(self.cgImage!, in: imageRect)
65 |
66 | var effectInBuffer = createEffectBuffer(context: effectInContext)
67 |
68 |
69 |
70 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
71 | let effectOutContext = UIGraphicsGetCurrentContext()!
72 |
73 | var effectOutBuffer = createEffectBuffer(context: effectOutContext)
74 |
75 |
76 | if hasBlur {
77 | // A description of how to compute the box kernel width from the Gaussian
78 | // radius (aka standard deviation) appears in the SVG spec:
79 | // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
80 | //
81 | // For larger values of 's' (s >= 2.0), an approximation can be used: Three
82 | // successive box-blurs build a piece-wise quadratic convolution kernel, which
83 | // approximates the Gaussian kernel to within roughly 3%.
84 | //
85 | // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
86 | //
87 | // ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
88 | //
89 |
90 | let inputRadius = radius * screenScale
91 | var radius = UInt32(floor(Double(inputRadius * 0.75 * sqrt(2.0 * .pi) + 0.5)))
92 | if radius % 2 != 1 {
93 | radius += 1 // force radius to be odd so that the three box-blur methodology works.
94 | }
95 |
96 | let imageEdgeExtendFlags = vImage_Flags(kvImageEdgeExtend)
97 |
98 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
99 | vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
100 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
101 | }
102 |
103 | effectImage = UIGraphicsGetImageFromCurrentImageContext()!
104 |
105 | UIGraphicsEndImageContext()
106 | UIGraphicsEndImageContext()
107 | }
108 |
109 | // Set up output context.
110 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
111 | let outputContext = UIGraphicsGetCurrentContext()
112 | outputContext!.scaleBy(x: 1.0, y: -1.0)
113 | outputContext!.translateBy(x: 0, y: -size.height)
114 |
115 | // Draw base image.
116 | outputContext!.draw(self.cgImage!, in: imageRect)
117 |
118 | // Draw effect image.
119 | if hasBlur {
120 | outputContext!.saveGState()
121 | if let image = maskImage {
122 | let effectCGImage = effectImage.cgImage!.masking(image.cgImage!)
123 | if let effectCGImage = effectCGImage {
124 | effectImage = UIImage(cgImage: effectCGImage)
125 | }
126 | }
127 | outputContext!.draw(effectImage.cgImage!, in: imageRect)
128 | outputContext!.restoreGState()
129 | }
130 |
131 | // Output image is ready.
132 | let outputImage = UIGraphicsGetImageFromCurrentImageContext()
133 | UIGraphicsEndImageContext()
134 |
135 | return outputImage
136 | }
137 | }
138 |
139 |
--------------------------------------------------------------------------------
/OperationQueue.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/OperationQueue.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/OperationQueue.playground/playground.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/OperationQueue.playground/playground.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/OperationQueue.playground/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
14 |
15 |
19 |
20 |
24 |
25 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | E417AF641E324FEF00D8CF51 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E417AF631E324FEF00D8CF51 /* AppDelegate.swift */; };
11 | E417AF691E324FEF00D8CF51 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E417AF671E324FEF00D8CF51 /* Main.storyboard */; };
12 | E417AF6B1E324FEF00D8CF51 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E417AF6A1E324FEF00D8CF51 /* Assets.xcassets */; };
13 | E417AF6E1E324FEF00D8CF51 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E417AF6C1E324FEF00D8CF51 /* LaunchScreen.storyboard */; };
14 | E417AF761E32504500D8CF51 /* ImageTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E417AF751E32504500D8CF51 /* ImageTableViewController.swift */; };
15 | E417AF791E32529C00D8CF51 /* ImageTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E417AF781E32529C00D8CF51 /* ImageTableViewCell.swift */; };
16 | E4448D8A1E4F7F7400B45C56 /* GradientGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4448D891E4F7F7400B45C56 /* GradientGenerator.swift */; };
17 | E4448D8C1E4F7F8200B45C56 /* UIImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4448D8B1E4F7F8200B45C56 /* UIImageExtensions.swift */; };
18 | E4448D8E1E4F7F8E00B45C56 /* PostProcess.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4448D8D1E4F7F8E00B45C56 /* PostProcess.swift */; };
19 | E4448D911E4F7FD800B45C56 /* AsyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4448D901E4F7FD800B45C56 /* AsyncOperation.swift */; };
20 | E4448D931E4F7FEE00B45C56 /* ImageTakeOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4448D921E4F7FEE00B45C56 /* ImageTakeOperation.swift */; };
21 | E4448D951E4F7FFF00B45C56 /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4448D941E4F7FFF00B45C56 /* Filter.swift */; };
22 | E4448D971E4F800E00B45C56 /* ImageLoadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4448D961E4F800E00B45C56 /* ImageLoadOperation.swift */; };
23 | E4448D991E4F801E00B45C56 /* ImageOutputOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4448D981E4F801E00B45C56 /* ImageOutputOperation.swift */; };
24 | E4448D9B1E4F802700B45C56 /* PostProcessingImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4448D9A1E4F802700B45C56 /* PostProcessingImage.swift */; };
25 | E4448D9F1E4F806600B45C56 /* ImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4448D9E1E4F806600B45C56 /* ImageProvider.swift */; };
26 | /* End PBXBuildFile section */
27 |
28 | /* Begin PBXFileReference section */
29 | E417AF601E324FEF00D8CF51 /* GCDTableViewController.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GCDTableViewController.app; sourceTree = BUILT_PRODUCTS_DIR; };
30 | E417AF631E324FEF00D8CF51 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
31 | E417AF681E324FEF00D8CF51 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
32 | E417AF6A1E324FEF00D8CF51 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
33 | E417AF6D1E324FEF00D8CF51 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
34 | E417AF6F1E324FEF00D8CF51 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
35 | E417AF751E32504500D8CF51 /* ImageTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageTableViewController.swift; sourceTree = ""; };
36 | E417AF781E32529C00D8CF51 /* ImageTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageTableViewCell.swift; path = GCDTableViewController/ImageTableViewCell.swift; sourceTree = ""; };
37 | E4448D891E4F7F7400B45C56 /* GradientGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientGenerator.swift; sourceTree = ""; };
38 | E4448D8B1E4F7F8200B45C56 /* UIImageExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageExtensions.swift; sourceTree = ""; };
39 | E4448D8D1E4F7F8E00B45C56 /* PostProcess.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostProcess.swift; sourceTree = ""; };
40 | E4448D901E4F7FD800B45C56 /* AsyncOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncOperation.swift; sourceTree = ""; };
41 | E4448D921E4F7FEE00B45C56 /* ImageTakeOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageTakeOperation.swift; sourceTree = ""; };
42 | E4448D941E4F7FFF00B45C56 /* Filter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = ""; };
43 | E4448D961E4F800E00B45C56 /* ImageLoadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageLoadOperation.swift; sourceTree = ""; };
44 | E4448D981E4F801E00B45C56 /* ImageOutputOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageOutputOperation.swift; sourceTree = ""; };
45 | E4448D9A1E4F802700B45C56 /* PostProcessingImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostProcessingImage.swift; sourceTree = ""; };
46 | E4448D9E1E4F806600B45C56 /* ImageProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageProvider.swift; sourceTree = ""; };
47 | /* End PBXFileReference section */
48 |
49 | /* Begin PBXFrameworksBuildPhase section */
50 | E417AF5D1E324FEF00D8CF51 /* Frameworks */ = {
51 | isa = PBXFrameworksBuildPhase;
52 | buildActionMask = 2147483647;
53 | files = (
54 | );
55 | runOnlyForDeploymentPostprocessing = 0;
56 | };
57 | /* End PBXFrameworksBuildPhase section */
58 |
59 | /* Begin PBXGroup section */
60 | E417AF571E324FEF00D8CF51 = {
61 | isa = PBXGroup;
62 | children = (
63 | E417AF621E324FEF00D8CF51 /* GCDTableViewController */,
64 | E417AF771E32508F00D8CF51 /* Supporting Files */,
65 | E417AF611E324FEF00D8CF51 /* Products */,
66 | E417AF781E32529C00D8CF51 /* ImageTableViewCell.swift */,
67 | );
68 | sourceTree = "";
69 | };
70 | E417AF611E324FEF00D8CF51 /* Products */ = {
71 | isa = PBXGroup;
72 | children = (
73 | E417AF601E324FEF00D8CF51 /* GCDTableViewController.app */,
74 | );
75 | name = Products;
76 | sourceTree = "";
77 | };
78 | E417AF621E324FEF00D8CF51 /* GCDTableViewController */ = {
79 | isa = PBXGroup;
80 | children = (
81 | E417AF751E32504500D8CF51 /* ImageTableViewController.swift */,
82 | E417AF671E324FEF00D8CF51 /* Main.storyboard */,
83 | E4448D9E1E4F806600B45C56 /* ImageProvider.swift */,
84 | E4448D9C1E4F803400B45C56 /* Operations */,
85 | E4448D8F1E4F7F9600B45C56 /* ImageFiltering */,
86 | );
87 | path = GCDTableViewController;
88 | sourceTree = "";
89 | };
90 | E417AF771E32508F00D8CF51 /* Supporting Files */ = {
91 | isa = PBXGroup;
92 | children = (
93 | E417AF6F1E324FEF00D8CF51 /* Info.plist */,
94 | E417AF631E324FEF00D8CF51 /* AppDelegate.swift */,
95 | E417AF6A1E324FEF00D8CF51 /* Assets.xcassets */,
96 | E417AF6C1E324FEF00D8CF51 /* LaunchScreen.storyboard */,
97 | );
98 | name = "Supporting Files";
99 | path = GCDTableViewController;
100 | sourceTree = "";
101 | };
102 | E4448D8F1E4F7F9600B45C56 /* ImageFiltering */ = {
103 | isa = PBXGroup;
104 | children = (
105 | E4448D891E4F7F7400B45C56 /* GradientGenerator.swift */,
106 | E4448D8B1E4F7F8200B45C56 /* UIImageExtensions.swift */,
107 | E4448D8D1E4F7F8E00B45C56 /* PostProcess.swift */,
108 | );
109 | name = ImageFiltering;
110 | sourceTree = "";
111 | };
112 | E4448D9C1E4F803400B45C56 /* Operations */ = {
113 | isa = PBXGroup;
114 | children = (
115 | E4448D9D1E4F804300B45C56 /* Abstract */,
116 | E4448D961E4F800E00B45C56 /* ImageLoadOperation.swift */,
117 | E4448D941E4F7FFF00B45C56 /* Filter.swift */,
118 | E4448D981E4F801E00B45C56 /* ImageOutputOperation.swift */,
119 | E4448D9A1E4F802700B45C56 /* PostProcessingImage.swift */,
120 | );
121 | name = Operations;
122 | sourceTree = "";
123 | };
124 | E4448D9D1E4F804300B45C56 /* Abstract */ = {
125 | isa = PBXGroup;
126 | children = (
127 | E4448D901E4F7FD800B45C56 /* AsyncOperation.swift */,
128 | E4448D921E4F7FEE00B45C56 /* ImageTakeOperation.swift */,
129 | );
130 | name = Abstract;
131 | sourceTree = "";
132 | };
133 | /* End PBXGroup section */
134 |
135 | /* Begin PBXNativeTarget section */
136 | E417AF5F1E324FEF00D8CF51 /* GCDTableViewController */ = {
137 | isa = PBXNativeTarget;
138 | buildConfigurationList = E417AF721E324FEF00D8CF51 /* Build configuration list for PBXNativeTarget "GCDTableViewController" */;
139 | buildPhases = (
140 | E417AF5C1E324FEF00D8CF51 /* Sources */,
141 | E417AF5D1E324FEF00D8CF51 /* Frameworks */,
142 | E417AF5E1E324FEF00D8CF51 /* Resources */,
143 | );
144 | buildRules = (
145 | );
146 | dependencies = (
147 | );
148 | name = GCDTableViewController;
149 | productName = GCDTableViewController;
150 | productReference = E417AF601E324FEF00D8CF51 /* GCDTableViewController.app */;
151 | productType = "com.apple.product-type.application";
152 | };
153 | /* End PBXNativeTarget section */
154 |
155 | /* Begin PBXProject section */
156 | E417AF581E324FEF00D8CF51 /* Project object */ = {
157 | isa = PBXProject;
158 | attributes = {
159 | LastSwiftUpdateCheck = 0820;
160 | LastUpgradeCheck = 0820;
161 | ORGANIZATIONNAME = "Tatiana Kornilova";
162 | TargetAttributes = {
163 | E417AF5F1E324FEF00D8CF51 = {
164 | CreatedOnToolsVersion = 8.2.1;
165 | DevelopmentTeam = QLY879YR3Y;
166 | ProvisioningStyle = Automatic;
167 | };
168 | };
169 | };
170 | buildConfigurationList = E417AF5B1E324FEF00D8CF51 /* Build configuration list for PBXProject "GCDTableViewController" */;
171 | compatibilityVersion = "Xcode 3.2";
172 | developmentRegion = English;
173 | hasScannedForEncodings = 0;
174 | knownRegions = (
175 | en,
176 | Base,
177 | );
178 | mainGroup = E417AF571E324FEF00D8CF51;
179 | productRefGroup = E417AF611E324FEF00D8CF51 /* Products */;
180 | projectDirPath = "";
181 | projectRoot = "";
182 | targets = (
183 | E417AF5F1E324FEF00D8CF51 /* GCDTableViewController */,
184 | );
185 | };
186 | /* End PBXProject section */
187 |
188 | /* Begin PBXResourcesBuildPhase section */
189 | E417AF5E1E324FEF00D8CF51 /* Resources */ = {
190 | isa = PBXResourcesBuildPhase;
191 | buildActionMask = 2147483647;
192 | files = (
193 | E417AF6E1E324FEF00D8CF51 /* LaunchScreen.storyboard in Resources */,
194 | E417AF6B1E324FEF00D8CF51 /* Assets.xcassets in Resources */,
195 | E417AF691E324FEF00D8CF51 /* Main.storyboard in Resources */,
196 | );
197 | runOnlyForDeploymentPostprocessing = 0;
198 | };
199 | /* End PBXResourcesBuildPhase section */
200 |
201 | /* Begin PBXSourcesBuildPhase section */
202 | E417AF5C1E324FEF00D8CF51 /* Sources */ = {
203 | isa = PBXSourcesBuildPhase;
204 | buildActionMask = 2147483647;
205 | files = (
206 | E417AF791E32529C00D8CF51 /* ImageTableViewCell.swift in Sources */,
207 | E4448D991E4F801E00B45C56 /* ImageOutputOperation.swift in Sources */,
208 | E417AF641E324FEF00D8CF51 /* AppDelegate.swift in Sources */,
209 | E4448D8C1E4F7F8200B45C56 /* UIImageExtensions.swift in Sources */,
210 | E4448D8E1E4F7F8E00B45C56 /* PostProcess.swift in Sources */,
211 | E4448D931E4F7FEE00B45C56 /* ImageTakeOperation.swift in Sources */,
212 | E417AF761E32504500D8CF51 /* ImageTableViewController.swift in Sources */,
213 | E4448D8A1E4F7F7400B45C56 /* GradientGenerator.swift in Sources */,
214 | E4448D951E4F7FFF00B45C56 /* Filter.swift in Sources */,
215 | E4448D911E4F7FD800B45C56 /* AsyncOperation.swift in Sources */,
216 | E4448D9B1E4F802700B45C56 /* PostProcessingImage.swift in Sources */,
217 | E4448D9F1E4F806600B45C56 /* ImageProvider.swift in Sources */,
218 | E4448D971E4F800E00B45C56 /* ImageLoadOperation.swift in Sources */,
219 | );
220 | runOnlyForDeploymentPostprocessing = 0;
221 | };
222 | /* End PBXSourcesBuildPhase section */
223 |
224 | /* Begin PBXVariantGroup section */
225 | E417AF671E324FEF00D8CF51 /* Main.storyboard */ = {
226 | isa = PBXVariantGroup;
227 | children = (
228 | E417AF681E324FEF00D8CF51 /* Base */,
229 | );
230 | name = Main.storyboard;
231 | sourceTree = "";
232 | };
233 | E417AF6C1E324FEF00D8CF51 /* LaunchScreen.storyboard */ = {
234 | isa = PBXVariantGroup;
235 | children = (
236 | E417AF6D1E324FEF00D8CF51 /* Base */,
237 | );
238 | name = LaunchScreen.storyboard;
239 | sourceTree = "";
240 | };
241 | /* End PBXVariantGroup section */
242 |
243 | /* Begin XCBuildConfiguration section */
244 | E417AF701E324FEF00D8CF51 /* Debug */ = {
245 | isa = XCBuildConfiguration;
246 | buildSettings = {
247 | ALWAYS_SEARCH_USER_PATHS = NO;
248 | CLANG_ANALYZER_NONNULL = YES;
249 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
250 | CLANG_CXX_LIBRARY = "libc++";
251 | CLANG_ENABLE_MODULES = YES;
252 | CLANG_ENABLE_OBJC_ARC = YES;
253 | CLANG_WARN_BOOL_CONVERSION = YES;
254 | CLANG_WARN_CONSTANT_CONVERSION = YES;
255 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
256 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
257 | CLANG_WARN_EMPTY_BODY = YES;
258 | CLANG_WARN_ENUM_CONVERSION = YES;
259 | CLANG_WARN_INFINITE_RECURSION = YES;
260 | CLANG_WARN_INT_CONVERSION = YES;
261 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
262 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
263 | CLANG_WARN_UNREACHABLE_CODE = YES;
264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
265 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
266 | COPY_PHASE_STRIP = NO;
267 | DEBUG_INFORMATION_FORMAT = dwarf;
268 | ENABLE_STRICT_OBJC_MSGSEND = YES;
269 | ENABLE_TESTABILITY = YES;
270 | GCC_C_LANGUAGE_STANDARD = gnu99;
271 | GCC_DYNAMIC_NO_PIC = NO;
272 | GCC_NO_COMMON_BLOCKS = YES;
273 | GCC_OPTIMIZATION_LEVEL = 0;
274 | GCC_PREPROCESSOR_DEFINITIONS = (
275 | "DEBUG=1",
276 | "$(inherited)",
277 | );
278 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
279 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
280 | GCC_WARN_UNDECLARED_SELECTOR = YES;
281 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
282 | GCC_WARN_UNUSED_FUNCTION = YES;
283 | GCC_WARN_UNUSED_VARIABLE = YES;
284 | IPHONEOS_DEPLOYMENT_TARGET = 10.2;
285 | MTL_ENABLE_DEBUG_INFO = YES;
286 | ONLY_ACTIVE_ARCH = YES;
287 | SDKROOT = iphoneos;
288 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
289 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
290 | };
291 | name = Debug;
292 | };
293 | E417AF711E324FEF00D8CF51 /* Release */ = {
294 | isa = XCBuildConfiguration;
295 | buildSettings = {
296 | ALWAYS_SEARCH_USER_PATHS = NO;
297 | CLANG_ANALYZER_NONNULL = YES;
298 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
299 | CLANG_CXX_LIBRARY = "libc++";
300 | CLANG_ENABLE_MODULES = YES;
301 | CLANG_ENABLE_OBJC_ARC = YES;
302 | CLANG_WARN_BOOL_CONVERSION = YES;
303 | CLANG_WARN_CONSTANT_CONVERSION = YES;
304 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
305 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
306 | CLANG_WARN_EMPTY_BODY = YES;
307 | CLANG_WARN_ENUM_CONVERSION = YES;
308 | CLANG_WARN_INFINITE_RECURSION = YES;
309 | CLANG_WARN_INT_CONVERSION = YES;
310 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
311 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
312 | CLANG_WARN_UNREACHABLE_CODE = YES;
313 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
314 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
315 | COPY_PHASE_STRIP = NO;
316 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
317 | ENABLE_NS_ASSERTIONS = NO;
318 | ENABLE_STRICT_OBJC_MSGSEND = YES;
319 | GCC_C_LANGUAGE_STANDARD = gnu99;
320 | GCC_NO_COMMON_BLOCKS = YES;
321 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
322 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
323 | GCC_WARN_UNDECLARED_SELECTOR = YES;
324 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
325 | GCC_WARN_UNUSED_FUNCTION = YES;
326 | GCC_WARN_UNUSED_VARIABLE = YES;
327 | IPHONEOS_DEPLOYMENT_TARGET = 10.2;
328 | MTL_ENABLE_DEBUG_INFO = NO;
329 | SDKROOT = iphoneos;
330 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
331 | VALIDATE_PRODUCT = YES;
332 | };
333 | name = Release;
334 | };
335 | E417AF731E324FEF00D8CF51 /* Debug */ = {
336 | isa = XCBuildConfiguration;
337 | buildSettings = {
338 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
339 | DEVELOPMENT_TEAM = QLY879YR3Y;
340 | INFOPLIST_FILE = GCDTableViewController/Info.plist;
341 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
342 | PRODUCT_BUNDLE_IDENTIFIER = home.GCDTableViewController;
343 | PRODUCT_NAME = "$(TARGET_NAME)";
344 | SWIFT_VERSION = 3.0;
345 | };
346 | name = Debug;
347 | };
348 | E417AF741E324FEF00D8CF51 /* Release */ = {
349 | isa = XCBuildConfiguration;
350 | buildSettings = {
351 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
352 | DEVELOPMENT_TEAM = QLY879YR3Y;
353 | INFOPLIST_FILE = GCDTableViewController/Info.plist;
354 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
355 | PRODUCT_BUNDLE_IDENTIFIER = home.GCDTableViewController;
356 | PRODUCT_NAME = "$(TARGET_NAME)";
357 | SWIFT_VERSION = 3.0;
358 | };
359 | name = Release;
360 | };
361 | /* End XCBuildConfiguration section */
362 |
363 | /* Begin XCConfigurationList section */
364 | E417AF5B1E324FEF00D8CF51 /* Build configuration list for PBXProject "GCDTableViewController" */ = {
365 | isa = XCConfigurationList;
366 | buildConfigurations = (
367 | E417AF701E324FEF00D8CF51 /* Debug */,
368 | E417AF711E324FEF00D8CF51 /* Release */,
369 | );
370 | defaultConfigurationIsVisible = 0;
371 | defaultConfigurationName = Release;
372 | };
373 | E417AF721E324FEF00D8CF51 /* Build configuration list for PBXNativeTarget "GCDTableViewController" */ = {
374 | isa = XCConfigurationList;
375 | buildConfigurations = (
376 | E417AF731E324FEF00D8CF51 /* Debug */,
377 | E417AF741E324FEF00D8CF51 /* Release */,
378 | );
379 | defaultConfigurationIsVisible = 0;
380 | defaultConfigurationName = Release;
381 | };
382 | /* End XCConfigurationList section */
383 | };
384 | rootObject = E417AF581E324FEF00D8CF51 /* Project object */;
385 | }
386 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController.xcodeproj/project.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BestKora/Operation_OperatioQueue/fa74adad6dc9a725203002d1cb56bb888a4890a4/OperationTableViewController/GCDTableViewController.xcodeproj/project.xcworkspace/xcuserdata/tatianakornilova.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController.xcodeproj/xcuserdata/tatianakornilova.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController.xcodeproj/xcuserdata/tatianakornilova.xcuserdatad/xcschemes/GCDTableViewController.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController.xcodeproj/xcuserdata/tatianakornilova.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | GCDTableViewController.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | E417AF5F1E324FEF00D8CF51
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // GCDTableViewController
4 | //
5 | // Created by Tatiana Kornilova on 1/20/17.
6 | // Copyright © 2017 Tatiana Kornilova. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/AsyncOperation.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | open class AsyncOperation: Operation {
4 | public enum State: String {
5 | case ready, executing, finished
6 |
7 | fileprivate var keyPath: String {
8 | return "is" + rawValue.capitalized
9 | }
10 | }
11 |
12 | public var state = State.ready {
13 | willSet {
14 | willChangeValue(forKey: newValue.keyPath)
15 | willChangeValue(forKey: state.keyPath)
16 | }
17 | didSet {
18 | didChangeValue(forKey: oldValue.keyPath)
19 | didChangeValue(forKey: state.keyPath)
20 | }
21 | }
22 | }
23 |
24 |
25 | extension AsyncOperation {
26 | // NSOperation Overrides
27 | override open var isReady: Bool {
28 | return super.isReady && state == .ready
29 | }
30 |
31 | override open var isExecuting: Bool {
32 | return state == .executing
33 | }
34 |
35 | override open var isFinished: Bool {
36 | return state == .finished
37 | }
38 |
39 | override open var isAsynchronous: Bool {
40 | return true
41 | }
42 |
43 | override open func start() {
44 | if isCancelled {
45 | state = .finished
46 | return
47 | }
48 |
49 | main()
50 | state = .executing
51 | }
52 |
53 | open override func cancel() {
54 | super.cancel()
55 | state = .finished
56 | }
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/Filter.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | public class Filter : ImageTakeOperation {
4 |
5 | override public func main() {
6 | if isCancelled { return }
7 | guard let inputImage = inputImage else { return }
8 |
9 | if isCancelled { return }
10 | let mask = topAndBottomGradient(inputImage.size)
11 |
12 | if isCancelled { return }
13 | outputImage = inputImage.applyBlurWithRadius(6, maskImage: mask)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/GradientGenerator.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import UIKit
24 |
25 | public func topAndBottomGradient(_ size: CGSize, clearLocations: [CGFloat] = [0.4, 0.6], innerIntensity: CGFloat = 0.5) -> UIImage {
26 |
27 | let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: 0, space: CGColorSpaceCreateDeviceGray(), bitmapInfo: CGImageAlphaInfo.none.rawValue)
28 |
29 | let colors = [
30 | UIColor.white,
31 | UIColor(white: innerIntensity, alpha: 1.0),
32 | UIColor.black,
33 | UIColor(white: innerIntensity, alpha: 1.0),
34 | UIColor.white
35 | ].map { $0.cgColor }
36 | let colorLocations : [CGFloat] = [0, clearLocations[0], (clearLocations[0] + clearLocations[1]) / 2.0, clearLocations[1], 1]
37 |
38 | let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceGray(), colors: colors as CFArray, locations: colorLocations)
39 |
40 | let startPoint = CGPoint(x: 0, y: 0)
41 | let endPoint = CGPoint(x: 0, y: size.height)
42 | context?.drawLinearGradient(gradient!, start: startPoint, end: endPoint, options: CGGradientDrawingOptions())
43 |
44 | let cgImage = context?.makeImage()
45 |
46 | return UIImage(cgImage: cgImage!)
47 | }
48 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/ImageLoadOperation.swift:
--------------------------------------------------------------------------------
1 |
2 | import UIKit
3 |
4 | public class ImageLoadOperation: AsyncOperation {
5 | private var url: URL?
6 | fileprivate var outputImage: UIImage?
7 |
8 | public init(url: URL?) {
9 | self.url = url
10 | super.init()
11 | }
12 |
13 | override public func main() {
14 | if self.isCancelled { return}
15 | guard let imageURL = url else {return}
16 | let task = URLSession.shared.dataTask(with: imageURL){(data, response, error) in
17 | if self.isCancelled { return}
18 | if let data = data,
19 | let imageData = UIImage(data: data) {
20 | if self.isCancelled { return }
21 | self.outputImage = imageData
22 | }
23 | self.state = .finished
24 | }
25 | task.resume()
26 | }
27 | }
28 |
29 |
30 | extension ImageLoadOperation: ImagePass {
31 | var image: UIImage? {
32 | return outputImage
33 | }
34 | }
35 |
36 |
37 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/ImageOutputOperation.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | public class ImageOutputOperation: ImageTakeOperation {
4 |
5 | private let completion: (UIImage?) -> ()
6 |
7 | public init(completion: @escaping (UIImage?) -> ()) {
8 | self.completion = completion
9 | super.init(image: nil)
10 | }
11 |
12 | override public func main() {
13 | if isCancelled { completion(nil)}
14 | completion(inputImage)
15 | }
16 | }
17 |
18 |
19 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/ImageProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageProvider.swift
3 | // ConcurrencyDemo
4 | //
5 | // Created by Tatiana Kornilova on 2/8/17.
6 | //
7 |
8 | import UIKit
9 | class ImageProvider {
10 |
11 | private var operationQueue = OperationQueue ()
12 | let imageURLString: String
13 |
14 | init(imageURLString: String,completion: @escaping (UIImage?) -> ()) {
15 | self.imageURLString = imageURLString
16 | operationQueue.maxConcurrentOperationCount = 2
17 |
18 | guard let imageURL = URL(string: imageURLString) else {return}
19 |
20 | // Создаем операции
21 | let dataLoad = ImageLoadOperation(url: imageURL)
22 | let filter = Filter(image: nil)
23 | let output = ImageOutputOperation(completion: completion)
24 |
25 | let operations = [dataLoad, filter, output]
26 |
27 | // Добавляем dependencies
28 | filter.addDependency(dataLoad)
29 | output.addDependency(filter)
30 |
31 | operationQueue.addOperations(operations, waitUntilFinished: false)
32 | }
33 |
34 | func cancel() {
35 | operationQueue.cancelAllOperations()
36 | }
37 |
38 | }
39 |
40 | extension ImageProvider: Hashable {
41 | var hashValue: Int {
42 | return (imageURLString).hashValue
43 | }
44 | }
45 |
46 | func ==(lhs: ImageProvider, rhs: ImageProvider) -> Bool {
47 | return lhs.imageURLString == rhs.imageURLString
48 | }
49 |
50 |
51 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/ImageTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // imageTableViewCell.swift
3 | // Smashtag
4 | //
5 | // Created by Tatiana Kornilova on 7/6/15.
6 | // Copyright (c) 2015 Stanford University. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | class ImageTableViewCell: UITableViewCell {
11 |
12 | @IBOutlet weak var tImage: UIImageView!
13 | @IBOutlet weak var spinner: UIActivityIndicatorView!
14 |
15 | // MARK: - Public API
16 | var imageURLString: String? {
17 | didSet {
18 | if imageURLString != nil {
19 | updateImageViewWithImage(nil)
20 | }
21 | }
22 | }
23 |
24 | func updateImageViewWithImage(_ image: UIImage?) {
25 | if let image = image {
26 | tImage.image = image
27 | tImage.alpha = 0
28 | UIView.animate(withDuration: 0.2, animations: {
29 | self.tImage.alpha = 1.0
30 | self.spinner.alpha = 0
31 | }, completion: { _ in
32 | self.spinner.stopAnimating()
33 | })
34 |
35 | } else {
36 | tImage.image = nil
37 | tImage.alpha = 0
38 | spinner.alpha = 1.0
39 | spinner.startAnimating()
40 | }
41 | }
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/ImageTableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageTableViewController.swift
3 | // GCDTableViewController
4 | //
5 | // Created by Tatiana Kornilova on 1/20/17.
6 | // Copyright © 2017 Tatiana Kornilova. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ImageTableViewController: UITableViewController {
12 | var imageURLs: [String] =
13 | ["http://www.planetware.com/photos-large/F/france-paris-eiffel-tower.jpg",
14 | "http://adriatic-lines.com/wp-content/uploads/2015/04/canal-of-Venice.jpg",
15 | "http://bestkora.com/IosDeveloper/wp-content/uploads/2016/12/Screen-Shot-2017-01-17-at-9.33.52-PM.png",
16 | "http://www.picture-newsletter.com/arctic/arctic-02.jpg",
17 | "http://www.planetware.com/photos-large/F/france-paris-eiffel-tower.jpg",
18 | "http://www.picture-newsletter.com/arctic/arctic-16.jpg",
19 | "http://www.picture-newsletter.com/arctic/arctic-15.jpg",
20 | "http://www.picture-newsletter.com/arctic/arctic-12.jpg"]
21 |
22 | var imageProviders = Set()
23 |
24 | // MARK: - Table view data source
25 |
26 | override func tableView(_ tableView: UITableView,
27 | numberOfRowsInSection section: Int) -> Int {
28 | return imageURLs.count
29 | }
30 |
31 | override func tableView(_ tableView: UITableView,
32 | cellForRowAt indexPath: IndexPath) -> UITableViewCell {
33 | let cell = tableView.dequeueReusableCell(withIdentifier: "imageOperation",
34 | for: indexPath)
35 |
36 | if let cell = cell as? ImageTableViewCell {
37 | cell.imageURLString = imageURLs [indexPath.row]
38 | }
39 | return cell
40 | }
41 | }
42 |
43 | extension ImageTableViewController {
44 | override func tableView(_ tableView: UITableView,
45 | willDisplay cell: UITableViewCell,
46 | forRowAt indexPath: IndexPath) {
47 | guard let cell = cell as? ImageTableViewCell else { return }
48 |
49 | let imageProvider = ImageProvider(imageURLString:
50 | imageURLs[(indexPath as NSIndexPath).row]) {
51 | image in
52 | OperationQueue.main.addOperation {
53 | cell.updateImageViewWithImage(image)
54 | }
55 | }
56 | imageProviders.insert(imageProvider)
57 | }
58 |
59 | override func tableView(_ tableView: UITableView,
60 | didEndDisplaying cell: UITableViewCell,
61 | forRowAt indexPath: IndexPath) {
62 | guard let cell = cell as? ImageTableViewCell else { return }
63 | for provider in
64 | imageProviders.filter({ $0.imageURLString == cell.imageURLString }){
65 |
66 | provider.cancel()
67 | imageProviders.remove(provider)
68 | }
69 | }
70 | }
71 |
72 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/ImageTakeOperation.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | protocol ImagePass {
4 | var image: UIImage? { get }
5 | }
6 |
7 | open class ImageTakeOperation: Operation {
8 | var outputImage: UIImage?
9 | private let _inputImage: UIImage?
10 |
11 | public init(image: UIImage?) {
12 | _inputImage = image
13 | super.init()
14 | }
15 |
16 | public var inputImage: UIImage? {
17 | var image: UIImage?
18 | if let inputImage = _inputImage {
19 | image = inputImage
20 | } else if let dataProvider = dependencies
21 | .filter({ $0 is ImagePass })
22 | .first as? ImagePass {
23 | image = dataProvider.image
24 | }
25 | return image
26 | }
27 | }
28 |
29 | extension ImageTakeOperation: ImagePass {
30 | var image: UIImage? {
31 | return outputImage
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | NSAppTransportSecurity
32 |
33 | NSAllowsArbitraryLoads
34 |
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/PostProcess.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import CoreImage
24 | import UIKit
25 |
26 |
27 | // This is an implementation of a Core Image filter chain that returns a CGImage
28 | // backed UIImage. This is not usually the best approach - CIImage-backed
29 | // UIImages are more optimised to display straight on screen.
30 | func postProcessImage(_ image: UIImage) -> UIImage {
31 |
32 | guard let inputImage = CIImage(image: image) else { return image }
33 |
34 | // Create filter chain
35 | guard let photoFilter = CIFilter(name: "CIPhotoEffectInstant",
36 | withInputParameters: ["inputImage" : inputImage]),
37 | let photoOutput = photoFilter.outputImage,
38 | let vignetteFilter = CIFilter(name: "CIVignette",
39 | withInputParameters: ["inputRadius" : 1.75, "inputIntensity" : 1.0, "inputImage": photoOutput]),
40 | let filterOutput = vignetteFilter.outputImage else { return image }
41 |
42 | let ciContext = CIContext(options: nil)
43 |
44 | let cgImage = ciContext.createCGImage(filterOutput, from: inputImage.extent)
45 | return UIImage(cgImage: cgImage!)
46 | }
47 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/PostProcessingImage.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Razeware LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 |
23 | import Foundation
24 |
25 | public class PostProcessImageOperation: ImageTakeOperation {
26 |
27 | override public func main() {
28 | if isCancelled { return}
29 | guard let inputImage = inputImage else { return }
30 | outputImage = postProcessImage(inputImage)
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/OperationTableViewController/GCDTableViewController/UIImageExtensions.swift:
--------------------------------------------------------------------------------
1 | /*
2 | File: UIImage+ImageEffects.m
3 | Abstract: This is a category of UIImage that adds methods to apply blur and tint effects to an image. This is the code you’ll want to look out to find out how to use vImage to efficiently calculate a blur.
4 | Version: 1.0
5 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
6 | Inc. ("Apple") in consideration of your agreement to the following
7 | terms, and your use, installation, modification or redistribution of
8 | this Apple software constitutes acceptance of these terms. If you do
9 | not agree with these terms, please do not use, install, modify or
10 | redistribute this Apple software.
11 | In consideration of your agreement to abide by the following terms, and
12 | subject to these terms, Apple grants you a personal, non-exclusive
13 | license, under Apple's copyrights in this original Apple software (the
14 | "Apple Software"), to use, reproduce, modify and redistribute the Apple
15 | Software, with or without modifications, in source and/or binary forms;
16 | provided that if you redistribute the Apple Software in its entirety and
17 | without modifications, you must retain this notice and the following
18 | text and disclaimers in all such redistributions of the Apple Software.
19 | Neither the name, trademarks, service marks or logos of Apple Inc. may
20 | be used to endorse or promote products derived from the Apple Software
21 | without specific prior written permission from Apple. Except as
22 | expressly stated in this notice, no other rights or licenses, express or
23 | implied, are granted by Apple herein, including but not limited to any
24 | patent rights that may be infringed by your derivative works or by other
25 | works in which the Apple Software may be incorporated.
26 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE
27 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
28 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
29 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
30 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
31 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
32 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
35 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
36 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
37 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
38 | POSSIBILITY OF SUCH DAMAGE.
39 | Copyright (C) 2013 Apple Inc. All Rights Reserved.
40 | Copyright © 2013 Apple Inc. All rights reserved.
41 | WWDC 2013 License
42 | NOTE: This Apple Software was supplied by Apple as part of a WWDC 2013
43 | Session. Please refer to the applicable WWDC 2013 Session for further
44 | information.
45 | IMPORTANT: This Apple software is supplied to you by Apple Inc.
46 | ("Apple") in consideration of your agreement to the following terms, and
47 | your use, installation, modification or redistribution of this Apple
48 | software constitutes acceptance of these terms. If you do not agree with
49 | these terms, please do not use, install, modify or redistribute this
50 | Apple software.
51 | In consideration of your agreement to abide by the following terms, and
52 | subject to these terms, Apple grants you a non-exclusive license, under
53 | Apple's copyrights in this original Apple software (the "Apple
54 | Software"), to use, reproduce, modify and redistribute the Apple
55 | Software, with or without modifications, in source and/or binary forms;
56 | provided that if you redistribute the Apple Software in its entirety and
57 | without modifications, you must retain this notice and the following
58 | text and disclaimers in all such redistributions of the Apple Software.
59 | Neither the name, trademarks, service marks or logos of Apple Inc. may
60 | be used to endorse or promote products derived from the Apple Software
61 | without specific prior written permission from Apple. Except as
62 | expressly stated in this notice, no other rights or licenses, express or
63 | implied, are granted by Apple herein, including but not limited to any
64 | patent rights that may be infringed by your derivative works or by other
65 | works in which the Apple Software may be incorporated.
66 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES
67 | NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
68 | IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR
69 | A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
70 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
71 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
72 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
73 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
74 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
75 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
76 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
77 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
78 | POSSIBILITY OF SUCH DAMAGE.
79 | EA1002
80 | 5/3/2013
81 | */
82 |
83 | //
84 | // UIImage.swift
85 | // Today
86 | //
87 | // Created by Alexey Globchastyy on 15/09/14.
88 | // Copyright (c) 2014 Alexey Globchastyy. All rights reserved.
89 | //
90 |
91 |
92 | import UIKit
93 | import Accelerate
94 |
95 | extension UIImage {
96 | public func applyBlurWithRadius(_ blurRadius: CGFloat, maskImage: UIImage? = nil) -> UIImage? {
97 | // Check pre-conditions.
98 | if (size.width < 1 || size.height < 1) {
99 | print("*** error: invalid size: \(size.width) x \(size.height). Both dimensions must be >= 1: \(self)")
100 | return nil
101 | }
102 | if self.cgImage == nil {
103 | print("*** error: image must be backed by a CGImage: \(self)")
104 | return nil
105 | }
106 | if maskImage != nil && maskImage!.cgImage == nil {
107 | print("*** error: maskImage must be backed by a CGImage: \(String(describing: maskImage))")
108 | return nil
109 | }
110 |
111 | let __FLT_EPSILON__ = CGFloat(Float.ulpOfOne)
112 | let screenScale = UIScreen.main.scale
113 | let imageRect = CGRect(origin: CGPoint.zero, size: size)
114 | var effectImage = self
115 |
116 | let hasBlur = blurRadius > __FLT_EPSILON__
117 |
118 | if hasBlur {
119 | func createEffectBuffer(_ context: CGContext) -> vImage_Buffer {
120 | let data = context.data
121 | let width = vImagePixelCount(context.width)
122 | let height = vImagePixelCount(context.height)
123 | let rowBytes = context.bytesPerRow
124 |
125 | return vImage_Buffer(data: data, height: height, width: width, rowBytes: rowBytes)
126 | }
127 |
128 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
129 | let effectInContext = UIGraphicsGetCurrentContext()!
130 |
131 | effectInContext.scaleBy(x: 1.0, y: -1.0)
132 | effectInContext.translateBy(x: 0, y: -size.height)
133 | effectInContext.draw(self.cgImage!, in: imageRect)
134 |
135 | var effectInBuffer = createEffectBuffer(effectInContext)
136 |
137 |
138 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
139 | let effectOutContext = UIGraphicsGetCurrentContext()!
140 |
141 | var effectOutBuffer = createEffectBuffer(effectOutContext)
142 |
143 |
144 | if hasBlur {
145 | // A description of how to compute the box kernel width from the Gaussian
146 | // radius (aka standard deviation) appears in the SVG spec:
147 | // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
148 | //
149 | // For larger values of 's' (s >= 2.0), an approximation can be used: Three
150 | // successive box-blurs build a piece-wise quadratic convolution kernel, which
151 | // approximates the Gaussian kernel to within roughly 3%.
152 | //
153 | // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
154 | //
155 | // ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
156 | //
157 |
158 | let inputRadius = blurRadius * screenScale
159 | var radius = UInt32(floor(Double(inputRadius * 0.75 * sqrt(2.0 * .pi) + 0.5)))
160 | if radius % 2 != 1 {
161 | radius += 1 // force radius to be odd so that the three box-blur methodology works.
162 | }
163 |
164 | let imageEdgeExtendFlags = vImage_Flags(kvImageEdgeExtend)
165 |
166 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
167 | vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
168 | vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
169 | }
170 |
171 | effectImage = UIGraphicsGetImageFromCurrentImageContext()!
172 |
173 | UIGraphicsEndImageContext()
174 | UIGraphicsEndImageContext()
175 | }
176 |
177 | // Set up output context.
178 | UIGraphicsBeginImageContextWithOptions(size, false, screenScale)
179 | let outputContext = UIGraphicsGetCurrentContext()
180 | outputContext?.scaleBy(x: 1.0, y: -1.0)
181 | outputContext?.translateBy(x: 0, y: -size.height)
182 |
183 | // Draw base image.
184 | outputContext?.draw(self.cgImage!, in: imageRect)
185 |
186 | // Draw effect image.
187 | if hasBlur {
188 | outputContext?.saveGState()
189 | if let image = maskImage {
190 | //CGContextClipToMask(outputContext, imageRect, image.CGImage);
191 | let effectCGImage = effectImage.cgImage?.masking(image.cgImage!)
192 | if let effectCGImage = effectCGImage {
193 | effectImage = UIImage(cgImage: effectCGImage)
194 | }
195 | }
196 | outputContext?.draw(effectImage.cgImage!, in: imageRect)
197 | outputContext?.restoreGState()
198 | }
199 |
200 | // Output image is ready.
201 | let outputImage = UIGraphicsGetImageFromCurrentImageContext()
202 | UIGraphicsEndImageContext()
203 |
204 | return outputImage
205 | }
206 | }
207 |
--------------------------------------------------------------------------------