【问题标题】:UI freezes for 5 sec after I dismiss UIViewController关闭 UIViewController 后 UI 冻结 5 秒
【发布时间】:2016-04-23 19:18:17
【问题描述】:

我有一个UICOLLECTIONVIEW,即在选择任何图像时,我打开的完整图像视图。 打开完整图像工作正常,但一旦我关闭它,整个应用程序冻结超过 5 秒。我读到主线程可能过载,这可以解释延迟,但我不确定要移动到哪些线程以及移动到哪里,如果这是问题。

import UIKit

class FullImageViewController: UIViewController , UIScrollViewDelegate {

    var scroller: UIScrollView!
    var width = CGFloat()
    var images = [Image]()
    var imageArray = [UIImage]()
    var imgs: [UIImage] = [UIImage(named: "pug7")!, UIImage(named: "pug6")!, UIImage(named: "pug7")!, UIImage(named: "pug6")!]
    var index = Int()
    var fullImagesAdded = Bool()

    init(width: CGFloat, images: [Image], index: Int, imageArray: [UIImage]) {

        super.init(nibName: nil, bundle: nil)
        self.width = width
        self.images = images
        self.index = index
        self.imageArray = imageArray
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let downSwipe = UISwipeGestureRecognizer(target: self, action: #selector(FullImageViewController.swipeDown))
        downSwipe.direction = .Down
        view.addGestureRecognizer(downSwipe)

        self.scroller = UIScrollView()
        self.scroller.delegate = self
        var i = 0
        for img in images {
            let aimageDetailViewController = ImageDetailViewController(index: self.index, image: img)
            self.scroller.addSubview(aimageDetailViewController.view)
            var aimageDetailViewFrame: CGRect = aimageDetailViewController.view.frame
            aimageDetailViewFrame.origin.x = (super.view.frame.width) * CGFloat(i)
            aimageDetailViewController.view.frame = aimageDetailViewFrame

            i += 1
        }

        self.scroller.backgroundColor = UIColor.blackColor()
        self.scroller.contentSize = CGSizeMake(self.view.frame.width * CGFloat(images.count), self.view.frame.height)
        self.scroller.bounces = false
        self.scroller.showsHorizontalScrollIndicator = false
        self.scroller.setContentOffset(CGPoint(x: self.view.frame.width * CGFloat(index), y: 0), animated: false)
        self.scroller.pagingEnabled = true

        self.view.addSubview(scroller)
    }

    func swipeDown(){
        dispatch_async(dispatch_get_main_queue()) {
            self.dismissViewControllerAnimated(true, completion: nil)
        }
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        scroller.frame = view.bounds
    }
}

这是我在 UICollectionView 中调用完整图像视图的地方:

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
    let fullImage = FullImageViewController(width: CGFloat(imageURLSCache.count), images: imageURLSCache, index: Int(indexPath.row), imageArray: images)
    self.presentViewController(fullImage, animated: true, completion: nil)
}

谢谢!

这是我用来下载完整图像视图和集合视图图像的扩展

extension UIImageView {

func downloadFrom(link link:String?, cache: NSCache ) {
    image = UIImage(named: "logo")
    self.alpha = 0.3
    let data: NSData? = cache.objectForKey(link!) as? NSData


    if let cachedData = data {
        let image = UIImage(data: cachedData)
        dispatch_async(dispatch_get_main_queue(), {() in
            self.image = image
            self.alpha = 1
            images.append(self.image!)
        })
        return
    }


    if link != nil, let url = NSURL(string: link!) {
        NSURLSession.sharedSession().dataTaskWithURL(url) { data, response, error in
            guard let data = data where error == nil else {
                print("\nerror on download \(error)")
                return
            }
            if let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode != 200 {
                print("statusCode != 200; \(httpResponse.statusCode)")
                return
            }
            dispatch_async(dispatch_get_main_queue()) {
                print("\ndownload completed \(url.lastPathComponent!)")
                self.image = UIImage(data: data)
                self.alpha = 1
                cache.setObject(data, forKey: link!)
            }
            }.resume()
    } else {
        dispatch_async(dispatch_get_main_queue(), {
            self.image = UIImage(named: "logo")
        })

    }
}

}

【问题讨论】:

    标签: swift uiviewcontroller uicollectionview


    【解决方案1】:
    func swipeDown(){
        dispatch_async(dispatch_get_main_queue()) {
            self.dismissViewControllerAnimated(true, completion: nil)
        }
    }
    

    此方法已设置为您的UISwipeGestureRecognizer 的操作。手势识别器将在主线程上调用它,因此您无需将其分派到主队列。这会增加延迟,具体取决于该队列的繁忙程度。

    将其更改为:

    func swipeDown() {
        self.dismissViewControllerAnimated(true, completion: nil)
    }
    

    关于您添加的其他信息的更新:

    UIImageView 上的扩展名不是很好(即,它假定 link 不是 nil 时将其用作缓存的键,但随后检查它是否是 nil,它可以'不是,否则你已经崩溃了)。但它至少确实使用dataTaskWithURL 方法在后台进行下载。

    我会尝试在调度到后台调用中调用它,如下所示,以确保您不会使用缓存检查等使主线程超载。我假设您正在安全地为此扩展创建缓存,当然还有重用所有调用的缓存,而不是为每个调用创建一个新的缓存(我已经看到了)。

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
        imageView.downloadFrom(....)
    }
    

    这是一个可供使用的替代扩展。这是我用过很多的一个。它为您处理创建缓存,因此您不必担心在扩展之外执行此操作。它还具有确认图像开始下载以来尚未在图像上设置另一个 URL 的好处。例如,当图像视图位于可重复使用的单元格上时,这一点很重要。创建单元格,将图像 url 设置为 x,当它仍在下载时,单元格被滚动出屏幕并重复使用,图像 url 设置为 y,然后 x 下载完成并显示用于旧单元格的图像。

    代码如下:

    //  UIImageView+XCGAdditions.swift
    //  Cerebral Gardens
    //
    //  Created by Dave Wood
    //  Copyright © Cerebral Gardens. All rights reserved.
    
    import UIKit
    
    extension UIImageView {
    
        func setImageWithLink(link: String) {
            guard let url = NSURL(string: link) else { return }
            setImageWithURL(url: url)
        }
    
        func setImageWithURL(url url: NSURL) {
            let checkInt = Int(arc4random_uniform(100000))
            tag = checkInt
    
            struct Statics {
                static var onceToken: dispatch_once_t = 0
                static var imageCache: NSCache!
            }
            dispatch_once(&Statics.onceToken) { () -> Void in
                Statics.imageCache = NSCache()
            }
    
            if let cachedImage = Statics.imageCache.objectForKey(url) as? UIImage {
                self.image = cachedImage
                return
            }
    
            NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data: NSData?, response: NSURLResponse?, error: NSError?) -> () in
                guard let data = data where error == nil,
                  let image = UIImage(data: data) where self.tag == checkInt else { return }
    
                dispatch_async(dispatch_get_main_queue()) { () -> () in
                    Statics.imageCache.setObject(image, forKey: url)
                    self.image = image
                }
            }).resume()
        }
    }
    

    【讨论】:

    • 感谢您的提示!更改后延迟仍然持续存在,您怀疑还有其他原因吗?谢谢!
    • 我在您发布的代码中没有看到任何其他延迟。当视图控制器再次出现在屏幕上时(通过viewWill/DidAppear() 等),呈现视图控制器中的某些内容可能会被调用。
    • 我呈现的视图有我下载的图片,当我关闭时,其中大部分还没有下载,这是一个原因吗?
    • 是的,如果您在主线程上下载它们,这将阻止所有 UI 更新并冻结应用程序。是否要显示下载过程的代码?
    • @Nch 如果您仍然看到问题,您可能有其他地方正在占用主线程。您需要进行大量调试并缩小范围。使用我的 XCGLogger 库 github.com/DaveWoodCom/XCGLogger 并启用线程名称可能会有所帮助。将日志放入任何可能占用 CPU 资源的函数中,并确保它在运行时不在主线程上。
    猜你喜欢
    • 1970-01-01
    • 2015-02-21
    • 2018-11-19
    • 1970-01-01
    • 2013-01-04
    • 2015-03-29
    • 1970-01-01
    • 2013-01-12
    • 1970-01-01
    相关资源
    最近更新 更多