Swift 2 reloadData 向 UICollectionView 添加更多单元格
Swift 2 reloadData adds more cells to the UICollectionView
我的应用程序中有一个烦人的错误,每次我加载 UICollectionView
控制器时,它都会复制单元格。
所以这是第一次加载集合视图的时间:
我有 2 个视图控制器,所以这是第二个视图控制器。如果我回到第一个视图控制器,它只有一个按钮到第二个视图控制器,然后再次转到触发 reloadData 函数的第二个视图控制器,就会发生这种情况:
我不太确定为什么会这样,我正在使用 Todd Kramer 的 UICollectionView
和缓存图像:http://www.tekramer.com/downloading-images-asynchronously-in-swift-with-alamofire/ 唯一的区别是我正在从中加载图像 url 和数据JSON 异步而不是像教程那样在模型或 plist 中静态定义。
以下是 类 我在代码中所做的更改:
import UIKit
import SwiftyJSON
private let PhotoCollectionViewCellIdentifier = "cell"
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
@IBOutlet weak var collectionView: UICollectionView!
var index: NSIndexPath!
var names = [String]()
var imgsUrl = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let photo = PhotosDataManager()
let api = Api()
api.loadJsonData(self.names, imgsUrl: self.imgsUrl, batch: "2016", dept: "dafa") { names, imgsUrl in
self.names = names
self.imgsUrl = imgsUrl
photo.allPhotos(self.names, imgUrls: self.imgsUrl)
self.collectionView.reloadData()
}
}
@IBAction func button(sender: UIButton) {
NSNotificationCenter.defaultCenter().postNotificationName("alert1", object: self, userInfo: nil)
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return PhotosDataManager.sharedManager.allPhotos(self.names, imgUrls: self.imgsUrl).count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(PhotoCollectionViewCellIdentifier, forIndexPath: indexPath) as! CollectionViewCell
dispatch_async(dispatch_get_main_queue(), {
cell.configure(self.glacierScenicAtIndex(indexPath))
})
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
PhotosDataManager.sharedManager.purgeCache()
self.performSegueWithIdentifier("previousViewController", sender: self)
}
func glacierScenicAtIndex(indexPath: NSIndexPath) -> GlacierScenic {
let photos = PhotosDataManager.sharedManager.allPhotos(self.names, imgUrls: self.imgsUrl)
return photos[indexPath.row]
}
}
class PhotosDataManager {
static let sharedManager = PhotosDataManager()
private var photos = [GlacierScenic]()
let decoder = ImageDecoder()
let photoCache = AutoPurgingImageCache(
memoryCapacity: 100 * 1024 * 1024,
preferredMemoryUsageAfterPurge: 60 * 1024 * 1024
)
func allPhotos(names: [String], imgUrls: [String]) -> [GlacierScenic] {
var glacierScenic: GlacierScenic!
for i in 0 ..< names.count {
glacierScenic = GlacierScenic(name: names[i], photoURLString: imgUrls[i])
photos.append(glacierScenic)
}
return photos
}
func getNetworkImage(urlString: String, completion: (UIImage -> Void)) -> (ImageRequest) {
let queue = decoder.queue.underlyingQueue
let request = Alamofire.request(.GET, urlString)
let imageRequest = ImageRequest(request: request)
imageRequest.request.response(
queue: queue,
responseSerializer: Request.imageResponseSerializer(),
completionHandler: { response in
guard let image = response.result.value else {
return
}
let decodeOperation = self.decodeImage(image) { image in
completion(image)
self.cacheImage(image, urlString: urlString)
}
imageRequest.decodeOperation = decodeOperation
}
)
return imageRequest
}
func decodeImage(image: UIImage, completion: (UIImage -> Void)) -> DecodeOperation {
let decodeOperation = DecodeOperation(image: image, decoder: self.decoder, completion: completion)
self.decoder.queue.addOperation(decodeOperation)
return decodeOperation
}
func cacheImage(image: Image, urlString: String) {
photoCache.addImage(image, withIdentifier: urlString)
}
func cachedImage(urlString: String) -> Image? {
return photoCache.imageWithIdentifier(urlString)
}
func purgeCache() {
// photoCache.removeAllImages()
print("memory used: \(photoCache.memoryUsage)")
}
}
class PhotosDataManager {
static let sharedManager = PhotosDataManager()
private var photos = [GlacierScenic]()
let decoder = ImageDecoder()
let photoCache = AutoPurgingImageCache(
memoryCapacity: 100 * 1024 * 1024,
preferredMemoryUsageAfterPurge: 60 * 1024 * 1024
)
func allPhotos(names: [String], imgUrls: [String]) -> [GlacierScenic] {
var glacierScenic: GlacierScenic!
for i in 0 ..< names.count {
glacierScenic = GlacierScenic(name: names[i], photoURLString: imgUrls[i])
photos.append(glacierScenic)
}
return photos
}
func getNetworkImage(urlString: String, completion: (UIImage -> Void)) -> (ImageRequest) {
let queue = decoder.queue.underlyingQueue
let request = Alamofire.request(.GET, urlString)
let imageRequest = ImageRequest(request: request)
imageRequest.request.response(
queue: queue,
responseSerializer: Request.imageResponseSerializer(),
completionHandler: { response in
guard let image = response.result.value else {
return
}
let decodeOperation = self.decodeImage(image) { image in
completion(image)
self.cacheImage(image, urlString: urlString)
}
imageRequest.decodeOperation = decodeOperation
}
)
return imageRequest
}
func decodeImage(image: UIImage, completion: (UIImage -> Void)) -> DecodeOperation {
let decodeOperation = DecodeOperation(image: image, decoder: self.decoder, completion: completion)
self.decoder.queue.addOperation(decodeOperation)
return decodeOperation
}
func cacheImage(image: Image, urlString: String) {
photoCache.addImage(image, withIdentifier: urlString)
}
func cachedImage(urlString: String) -> Image? {
return photoCache.imageWithIdentifier(urlString)
}
func purgeCache() {
// photoCache.removeAllImages()
print("memory used: \(photoCache.memoryUsage)")
}
}
问题
在 PhotosDataManager 中 => 您正在使用 sharedManager 引用,它将对象引用保存在静态引用中并继续重复使用它,在所有 segues 导航中,因此在您的 allPhotos 方法中,继续在旧数组中添加数据。
static let sharedManager = PhotosDataManager()
另一个问题
不要调用 numberOfItemsInSection 中的 allPhotos 方法,因为它会被多次调用,这可能会多次附加数据
解决方案
在你的方法中:
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//USE Instantiated reference
return photos.getPhotosCount()
}
相反,在您的 class class PhotosDataManager
中
class PhotosDataManager {
func getPhotosCount(){
return photos.count
}
}
我的应用程序中有一个烦人的错误,每次我加载 UICollectionView
控制器时,它都会复制单元格。
所以这是第一次加载集合视图的时间:
我有 2 个视图控制器,所以这是第二个视图控制器。如果我回到第一个视图控制器,它只有一个按钮到第二个视图控制器,然后再次转到触发 reloadData 函数的第二个视图控制器,就会发生这种情况:
我不太确定为什么会这样,我正在使用 Todd Kramer 的 UICollectionView
和缓存图像:http://www.tekramer.com/downloading-images-asynchronously-in-swift-with-alamofire/ 唯一的区别是我正在从中加载图像 url 和数据JSON 异步而不是像教程那样在模型或 plist 中静态定义。
以下是 类 我在代码中所做的更改:
import UIKit
import SwiftyJSON
private let PhotoCollectionViewCellIdentifier = "cell"
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
@IBOutlet weak var collectionView: UICollectionView!
var index: NSIndexPath!
var names = [String]()
var imgsUrl = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let photo = PhotosDataManager()
let api = Api()
api.loadJsonData(self.names, imgsUrl: self.imgsUrl, batch: "2016", dept: "dafa") { names, imgsUrl in
self.names = names
self.imgsUrl = imgsUrl
photo.allPhotos(self.names, imgUrls: self.imgsUrl)
self.collectionView.reloadData()
}
}
@IBAction func button(sender: UIButton) {
NSNotificationCenter.defaultCenter().postNotificationName("alert1", object: self, userInfo: nil)
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return PhotosDataManager.sharedManager.allPhotos(self.names, imgUrls: self.imgsUrl).count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(PhotoCollectionViewCellIdentifier, forIndexPath: indexPath) as! CollectionViewCell
dispatch_async(dispatch_get_main_queue(), {
cell.configure(self.glacierScenicAtIndex(indexPath))
})
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
PhotosDataManager.sharedManager.purgeCache()
self.performSegueWithIdentifier("previousViewController", sender: self)
}
func glacierScenicAtIndex(indexPath: NSIndexPath) -> GlacierScenic {
let photos = PhotosDataManager.sharedManager.allPhotos(self.names, imgUrls: self.imgsUrl)
return photos[indexPath.row]
}
}
class PhotosDataManager {
static let sharedManager = PhotosDataManager()
private var photos = [GlacierScenic]()
let decoder = ImageDecoder()
let photoCache = AutoPurgingImageCache(
memoryCapacity: 100 * 1024 * 1024,
preferredMemoryUsageAfterPurge: 60 * 1024 * 1024
)
func allPhotos(names: [String], imgUrls: [String]) -> [GlacierScenic] {
var glacierScenic: GlacierScenic!
for i in 0 ..< names.count {
glacierScenic = GlacierScenic(name: names[i], photoURLString: imgUrls[i])
photos.append(glacierScenic)
}
return photos
}
func getNetworkImage(urlString: String, completion: (UIImage -> Void)) -> (ImageRequest) {
let queue = decoder.queue.underlyingQueue
let request = Alamofire.request(.GET, urlString)
let imageRequest = ImageRequest(request: request)
imageRequest.request.response(
queue: queue,
responseSerializer: Request.imageResponseSerializer(),
completionHandler: { response in
guard let image = response.result.value else {
return
}
let decodeOperation = self.decodeImage(image) { image in
completion(image)
self.cacheImage(image, urlString: urlString)
}
imageRequest.decodeOperation = decodeOperation
}
)
return imageRequest
}
func decodeImage(image: UIImage, completion: (UIImage -> Void)) -> DecodeOperation {
let decodeOperation = DecodeOperation(image: image, decoder: self.decoder, completion: completion)
self.decoder.queue.addOperation(decodeOperation)
return decodeOperation
}
func cacheImage(image: Image, urlString: String) {
photoCache.addImage(image, withIdentifier: urlString)
}
func cachedImage(urlString: String) -> Image? {
return photoCache.imageWithIdentifier(urlString)
}
func purgeCache() {
// photoCache.removeAllImages()
print("memory used: \(photoCache.memoryUsage)")
}
}
class PhotosDataManager {
static let sharedManager = PhotosDataManager()
private var photos = [GlacierScenic]()
let decoder = ImageDecoder()
let photoCache = AutoPurgingImageCache(
memoryCapacity: 100 * 1024 * 1024,
preferredMemoryUsageAfterPurge: 60 * 1024 * 1024
)
func allPhotos(names: [String], imgUrls: [String]) -> [GlacierScenic] {
var glacierScenic: GlacierScenic!
for i in 0 ..< names.count {
glacierScenic = GlacierScenic(name: names[i], photoURLString: imgUrls[i])
photos.append(glacierScenic)
}
return photos
}
func getNetworkImage(urlString: String, completion: (UIImage -> Void)) -> (ImageRequest) {
let queue = decoder.queue.underlyingQueue
let request = Alamofire.request(.GET, urlString)
let imageRequest = ImageRequest(request: request)
imageRequest.request.response(
queue: queue,
responseSerializer: Request.imageResponseSerializer(),
completionHandler: { response in
guard let image = response.result.value else {
return
}
let decodeOperation = self.decodeImage(image) { image in
completion(image)
self.cacheImage(image, urlString: urlString)
}
imageRequest.decodeOperation = decodeOperation
}
)
return imageRequest
}
func decodeImage(image: UIImage, completion: (UIImage -> Void)) -> DecodeOperation {
let decodeOperation = DecodeOperation(image: image, decoder: self.decoder, completion: completion)
self.decoder.queue.addOperation(decodeOperation)
return decodeOperation
}
func cacheImage(image: Image, urlString: String) {
photoCache.addImage(image, withIdentifier: urlString)
}
func cachedImage(urlString: String) -> Image? {
return photoCache.imageWithIdentifier(urlString)
}
func purgeCache() {
// photoCache.removeAllImages()
print("memory used: \(photoCache.memoryUsage)")
}
}
问题
在 PhotosDataManager 中 => 您正在使用 sharedManager 引用,它将对象引用保存在静态引用中并继续重复使用它,在所有 segues 导航中,因此在您的 allPhotos 方法中,继续在旧数组中添加数据。
static let sharedManager = PhotosDataManager()
另一个问题
不要调用 numberOfItemsInSection 中的 allPhotos 方法,因为它会被多次调用,这可能会多次附加数据
解决方案
在你的方法中:
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//USE Instantiated reference
return photos.getPhotosCount()
}
相反,在您的 class class PhotosDataManager
中class PhotosDataManager {
func getPhotosCount(){
return photos.count
}
}