标签:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
< plist version = "1.0" > < dict > < key >港囧</ key > < key >第三种爱情</ key > < key >碟中谍5</ key > < key >小黄人大眼萌</ key > < key >夏洛特烦恼</ key > < key >魔镜</ key > < key >长江7号</ key > < key >九层妖塔</ key > < key >hangge.com</ key > < string >NO URL</ string > </ dict > </ plist > |
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
65
66
67
|
import UIKit import CoreImage //图片数据源地址 let dataSourcePath = NSBundle .mainBundle().pathForResource( "movies" , ofType: "plist" ) class ListViewController : UITableViewController { //电影图片字典集合(使用了懒加载) lazy var movies = NSDictionary (contentsOfFile: dataSourcePath!)! override func viewDidLoad() { super .viewDidLoad() self .title = "热门电影" } override func didReceiveMemoryWarning() { super .didReceiveMemoryWarning() } //获取记录数 override func tableView(tableView: UITableView ?, numberOfRowsInSection section: Int ) -> Int { return movies.count } //创建单元格 override func tableView(tableView: UITableView , cellForRowAtIndexPath indexPath: NSIndexPath ) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier( "CellIdentifier" , forIndexPath: indexPath) as UITableViewCell //设置单元格文本 let rowKey = movies.allKeys[indexPath.row] as ! String cell.textLabel?.text = rowKey //设置单元格图片 var image : UIImage ? if let imageURL = NSURL (string:movies[rowKey] as ! String ) { let imageData = NSData (contentsOfURL:imageURL) let unfilteredImage = UIImage (data:imageData!) image = self .applySepiaFilter(unfilteredImage!) } if image != nil { cell.imageView?.image = image! } else { //cell.imageView?.image = nil //未加载到海报则空白 cell.imageView?.image = UIImage (named: "failed" ) //未加载到海报显示默认的“暂无图片” } return cell } //给图片添加棕褐色滤镜 func applySepiaFilter(image: UIImage ) -> UIImage ? { let inputImage = CIImage (data: UIImagePNGRepresentation (image)!) let context = CIContext (options: nil ) let filter = CIFilter (name: "CISepiaTone" ) filter !.setValue(inputImage, forKey: kCIInputImageKey) filter !.setValue(0.8, forKey: "inputIntensity" ) if let outputImage = filter !.outputImage { let outImage = context.createCGImage(outputImage, fromRect: outputImage.extent) return UIImage ( CGImage : outImage) } return nil } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
import UIKit // 这个枚举包含所有电影图片的状态 enum MovieRecordState { case New , Downloaded , Filtered , Failed } // 电影条目类 class MovieRecord { let name: String let url: NSURL var state = MovieRecordState . New //默认初始图片 var image = UIImage (named: "placeholder" ) init (name: String , url: NSURL ) { self .name = name self .url = url } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
import Foundation //队列管理类,追踪每个操作的状态 class MovieOperations { //追踪进行中的和等待中的下载操作 lazy var downloadsInProgress = [ NSIndexPath : NSOperation ]() //图片下载队列 lazy var downloadQueue: NSOperationQueue = { var queue = NSOperationQueue () queue.name = "Download queue" queue.maxConcurrentOperationCount = 1 return queue }() //追踪进行中的和等待中的滤镜操作 lazy var filtrationsInProgress = [ NSIndexPath : NSOperation ]() //图片处理队列 lazy var filtrationQueue: NSOperationQueue = { var queue = NSOperationQueue () queue.name = "Image Filtration queue" queue.maxConcurrentOperationCount = 1 return queue }() } |
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
|
import UIKit //图片下载操作任务 class ImageDownloader : NSOperation { //电影条目对象 let movieRecord: MovieRecord init (movieRecord: MovieRecord ) { self .movieRecord = movieRecord } //在子类中重载NSOperation的main方法来执行实际的任务。 override func main() { //在开始执行前检查撤消状态。任务在试图执行繁重的工作前应该检查它是否已经被撤消。 if self .cancelled { return } //sleep(1) //这个只是为了便于测试观察 //下载图片。 let imageData = NSData (contentsOfURL: self .movieRecord.url) //再一次检查撤销状态。 if self .cancelled { return } //如果有数据,创建一个图片对象并加入记录,然后更改状态。如果没有数据,将记录标记为失败并设置失败图片。 if imageData?.length > 0 { self .movieRecord.image = UIImage (data:imageData!) self .movieRecord.state = . Downloaded } else { self .movieRecord.state = . Failed self .movieRecord.image = UIImage (named: "failed" ) } } } |
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
|
import UIKit import CoreImage //滤镜处理任务 class ImageFiltration : NSOperation { //电影条目对象 let movieRecord: MovieRecord init (movieRecord: MovieRecord ) { self .movieRecord = movieRecord } //在子类中重载NSOperation的main方法来执行实际的任务。 override func main () { if self .cancelled { return } if self .movieRecord.state != . Downloaded { return } if let filteredImage = self .applySepiaFilter( self .movieRecord.image!) { self .movieRecord.image = filteredImage self .movieRecord.state = . Filtered } } //给图片添加棕褐色滤镜 func applySepiaFilter(image: UIImage ) -> UIImage ? { let inputImage = CIImage (data: UIImagePNGRepresentation (image)!) if self .cancelled { return nil } let context = CIContext (options: nil ) let filter = CIFilter (name: "CISepiaTone" ) filter ?.setValue(inputImage, forKey: kCIInputImageKey) filter ?.setValue(0.8, forKey: "inputIntensity" ) let outputImage = filter ?.outputImage if self .cancelled { return nil } let outImage = context.createCGImage(outputImage!, fromRect: outputImage!.extent) let returnImage = UIImage ( CGImage : outImage) return returnImage } } |
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
import UIKit import CoreImage class ListViewController : UITableViewController { var movies = [ MovieRecord ]() let movieOperations = MovieOperations () override func viewDidLoad() { super .viewDidLoad() self .title = "热门电影" //加载,处理电影列表数据 fetchMovieDetails(); } override func didReceiveMemoryWarning() { super .didReceiveMemoryWarning() } //加载,处理电影列表数据 func fetchMovieDetails() { //图片数据源地址 let dataSourcePath = NSBundle .mainBundle().pathForResource( "movies" , ofType: "plist" ) let datasourceDictionary = NSDictionary (contentsOfFile: dataSourcePath!) for (key,value) in datasourceDictionary!{ let name = key as ? String let url = NSURL (string:value as ? String ?? "" ) if name != nil && url != nil { let movieRecord = MovieRecord (name:name!, url:url!) self .movies.append(movieRecord) } } self .tableView.reloadData() } //获取记录数 override func tableView(tableView: UITableView ?, numberOfRowsInSection section: Int ) -> Int { return movies.count } //创建单元格 override func tableView(tableView: UITableView , cellForRowAtIndexPath indexPath: NSIndexPath ) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier( "CellIdentifier" , forIndexPath: indexPath) as UITableViewCell //为了提示用户,将cell的accessory view设置为UIActivityIndicatorView。 if cell.accessoryView == nil { let indicator = UIActivityIndicatorView (activityIndicatorStyle: . Gray ) cell.accessoryView = indicator } let indicator = cell.accessoryView as ! UIActivityIndicatorView //获取当前行所对应的电影记录。 let movieRecord = movies[indexPath.row] //设置文本和图片 cell.textLabel?.text = movieRecord.name cell.imageView?.image = movieRecord.image //检查图片状态。设置适当的activity indicator 和文本,然后开始执行任务 switch (movieRecord.state){ case . Filtered : indicator.stopAnimating() case . Failed : indicator.stopAnimating() cell.textLabel?.text = "Failed to load" case . New : indicator.startAnimating() startDownloadForRecord(movieRecord, indexPath: indexPath) case . Downloaded : indicator.startAnimating() startFiltrationForRecord(movieRecord, indexPath: indexPath) } return cell } //执行图片下载任务 func startDownloadForRecord(movieRecord: MovieRecord , indexPath: NSIndexPath ){ //判断队列中是否已有该图片任务 if let _ = movieOperations.downloadsInProgress[indexPath] { return } //创建一个下载任务 let downloader = ImageDownloader (movieRecord: movieRecord) //任务完成后重新加载对应的单元格 downloader.completionBlock = { if downloader.cancelled { return } dispatch_async(dispatch_get_main_queue(), { self .movieOperations.downloadsInProgress.removeValueForKey(indexPath) self .tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: . Fade ) }) } //记录当前下载任务 movieOperations.downloadsInProgress[indexPath] = downloader //将任务添加到队列中 movieOperations.downloadQueue.addOperation(downloader) } //执行图片滤镜任务 func startFiltrationForRecord(movieRecord: MovieRecord , indexPath: NSIndexPath ){ if let _ = movieOperations.filtrationsInProgress[indexPath]{ return } let filterer = ImageFiltration (movieRecord: movieRecord) filterer.completionBlock = { if filterer.cancelled { return } dispatch_async(dispatch_get_main_queue(), { self .movieOperations.filtrationsInProgress.removeValueForKey(indexPath) self .tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: . Fade ) }) } movieOperations.filtrationsInProgress[indexPath] = filterer movieOperations.filtrationQueue.addOperation(filterer) } } |
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
|
import UIKit import CoreImage class ListViewController : UITableViewController { var movies = [ MovieRecord ]() let movieOperations = MovieOperations () override func viewDidLoad() { super .viewDidLoad() self .title = "热门电影" //加载,处理电影列表数据 fetchMovieDetails(); } override func didReceiveMemoryWarning() { super .didReceiveMemoryWarning() } //加载,处理电影列表数据 func fetchMovieDetails() { //图片数据源地址 let dataSourcePath = NSBundle .mainBundle().pathForResource( "movies" , ofType: "plist" ) let datasourceDictionary = NSDictionary (contentsOfFile: dataSourcePath!) for (key,value) in datasourceDictionary!{ let name = key as ? String let url = NSURL (string:value as ? String ?? "" ) if name != nil && url != nil { let movieRecord = MovieRecord (name:name!, url:url!) self .movies.append(movieRecord) } } self .tableView.reloadData() } //获取记录数 override func tableView(tableView: UITableView ?, numberOfRowsInSection section: Int ) -> Int { return movies.count } //创建单元格 override func tableView(tableView: UITableView , cellForRowAtIndexPath indexPath: NSIndexPath ) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier( "CellIdentifier" , forIndexPath: indexPath) as UITableViewCell //为了提示用户,将cell的accessory view设置为UIActivityIndicatorView。 if cell.accessoryView == nil { let indicator = UIActivityIndicatorView (activityIndicatorStyle: . Gray ) cell.accessoryView = indicator } let indicator = cell.accessoryView as ! UIActivityIndicatorView //获取当前行所对应的电影记录。 let movieRecord = movies[indexPath.row] //设置文本和图片 cell.textLabel?.text = movieRecord.name cell.imageView?.image = movieRecord.image //检查图片状态。设置适当的activity indicator 和文本,然后开始执行任务 switch (movieRecord.state){ case . Filtered : indicator.stopAnimating() case . Failed : indicator.stopAnimating() cell.textLabel?.text = "Failed to load" case . New , . Downloaded : indicator.startAnimating() //只有停止拖动的时候才加载 if (!tableView.dragging && !tableView.decelerating) { self .startOperationsForMovieRecord(movieRecord, indexPath: indexPath) } } return cell } //图片任务 func startOperationsForMovieRecord(movieRecord: MovieRecord , indexPath: NSIndexPath ){ switch (movieRecord.state) { case . New : startDownloadForRecord(movieRecord, indexPath: indexPath) case . Downloaded : startFiltrationForRecord(movieRecord, indexPath: indexPath) default : NSLog ( "do nothing" ) } } //执行图片下载任务 func startDownloadForRecord(movieRecord: MovieRecord , indexPath: NSIndexPath ){ //判断队列中是否已有该图片任务 if let _ = movieOperations.downloadsInProgress[indexPath] { return } //创建一个下载任务 let downloader = ImageDownloader (movieRecord: movieRecord) //任务完成后重新加载对应的单元格 downloader.completionBlock = { if downloader.cancelled { return } dispatch_async(dispatch_get_main_queue(), { self .movieOperations.downloadsInProgress.removeValueForKey(indexPath) self .tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: . Fade ) }) } //记录当前下载任务 movieOperations.downloadsInProgress[indexPath] = downloader //将任务添加到队列中 movieOperations.downloadQueue.addOperation(downloader) } //执行图片滤镜任务 func startFiltrationForRecord(movieRecord: MovieRecord , indexPath: NSIndexPath ){ if let _ = movieOperations.filtrationsInProgress[indexPath]{ return } let filterer = ImageFiltration (movieRecord: movieRecord) filterer.completionBlock = { if filterer.cancelled { return } dispatch_async(dispatch_get_main_queue(), { self .movieOperations.filtrationsInProgress.removeValueForKey(indexPath) self .tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: . Fade ) }) } movieOperations.filtrationsInProgress[indexPath] = filterer movieOperations.filtrationQueue.addOperation(filterer) } //视图开始滚动 override func scrollViewWillBeginDragging(scrollView: UIScrollView ) { //一旦用户开始滚动屏幕,你将挂起所有任务并留意用户想要看哪些行。 suspendAllOperations() } //视图停止拖动 override func scrollViewDidEndDragging(scrollView: UIScrollView , willDecelerate decelerate: Bool ) { //如果减速(decelerate)是 false ,表示用户停止拖拽tableview。 //此时你要继续执行之前挂起的任务,撤销不在屏幕中的cell的任务并开始在屏幕中的cell的任务。 if !decelerate { loadImagesForOnscreenCells() resumeAllOperations() } } //视图停止减速 override func scrollViewDidEndDecelerating(scrollView: UIScrollView ) { //这个代理方法告诉你tableview停止滚动,执行操作同上 loadImagesForOnscreenCells() resumeAllOperations() } //暂停所有队列 func suspendAllOperations () { movieOperations.downloadQueue.suspended = true movieOperations.filtrationQueue.suspended = true } //恢复运行所有队列 func resumeAllOperations () { movieOperations.downloadQueue.suspended = false movieOperations.filtrationQueue.suspended = false } //加载可见区域的单元格图片 func loadImagesForOnscreenCells () { //开始将tableview可见行的index path放入数组中。 if let pathsArray = self .tableView.indexPathsForVisibleRows { //通过组合所有下载队列和滤镜队列中的任务来创建一个包含所有等待任务的集合 let allMovieOperations = NSMutableSet () for key in movieOperations.downloadsInProgress.keys{ allMovieOperations.addObject(key) } for key in movieOperations.filtrationsInProgress.keys{ allMovieOperations.addObject(key) } //构建一个需要撤销的任务的集合。从所有任务中除掉可见行的index path, //剩下的就是屏幕外的行所代表的任务。 let toBeCancelled = allMovieOperations.mutableCopy() as ! NSMutableSet let visiblePaths = NSSet (array: pathsArray) toBeCancelled.minusSet(visiblePaths as Set < NSObject >) //创建一个需要执行的任务的集合。从所有可见index path的集合中除去那些已经在等待队列中的。 let toBeStarted = visiblePaths.mutableCopy() as ! NSMutableSet toBeStarted.minusSet(allMovieOperations as Set < NSObject >) // 遍历需要撤销的任务,撤消它们,然后从 movieOperations 中去掉它们 for indexPath in toBeCancelled { let indexPath = indexPath as ! NSIndexPath if let movieDownload = movieOperations.downloadsInProgress[indexPath] { movieDownload.cancel() } movieOperations.downloadsInProgress.removeValueForKey(indexPath) if let movieFiltration = movieOperations.filtrationsInProgress[indexPath] { movieFiltration.cancel() } movieOperations.filtrationsInProgress.removeValueForKey(indexPath) } // 遍历需要开始的任务,调用 startOperationsForPhotoRecord for indexPath in toBeStarted { let indexPath = indexPath as ! NSIndexPath let recordToProcess = self .movies[indexPath.row] startOperationsForMovieRecord(recordToProcess, indexPath: indexPath) } } } } |
Swift - 表格图片加载优化(拖动表格时不加载,停止时只加载当前页图片)
标签:
原文地址:http://www.cnblogs.com/Free-Thinker/p/4858383.html