UIPickerView 在重新加载时冻结 UI

Posted

技术标签:

【中文标题】UIPickerView 在重新加载时冻结 UI【英文标题】:UIPickerView freezes UI when reloaded 【发布时间】:2020-05-28 06:47:18 【问题描述】:

当 UIPickerView 的索引为 1 或元素数为 2 时,我的应用程序的 ui 冻结。我真的很困惑,因为它正在工作,然后突然就坏了。我做了一些研究,发现它可能与线程有关。有人可以帮我解决 UIPickerView 的问题吗?

// class implements UIPickerViewDelegate, UIPickerViewDataSource, UIGestureRecognizerDelegate

     @IBOutlet weak var planetPickerView: UIView!
     var planetPicker: UIPickerView!

视图已加载:

override func viewDidLoad() 
     super.viewDidLoad()
     //Other code not important 
     configurePlanetPicker()

UIPickerView 实现:(skyBalls 是一个类数组,其中包含各种数据作为 getter)

 func configurePlanetPicker() 
        planetPicker = UIPickerView(frame: CGRect(x: 0, y: 0, width: planetPickerView.frame.width, height: 70))
        planetPicker.delegate = self
        planetPicker.dataSource = self
        planetPicker.transform = CGAffineTransform(rotationAngle: CGFloat(-90 * (Double.pi/180)))
        planetPicker.frame = CGRect(x: -100, y: 0, width: planetPickerView.frame.width + 200, height: 70)
        planetPicker.showsSelectionIndicator = false
        planetPickerView.backgroundColor = .clear
        let tap = UITapGestureRecognizer(target: self, action: #selector(pickerTapped))
        tap.delegate = self
        planetPicker.addGestureRecognizer(tap)
        planetPickerView.addSubview(planetPicker)
    

    func numberOfComponents(in pickerView: UIPickerView) -> Int  return 1 

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int return skyBalls.count 

    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat  return 70 

    func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat  return 70 

    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView 
        let mainView = UIView(frame: CGRect(x: 0, y: 0, width: 70, height: 70))

        let image = UIImageView(frame: CGRect(x: 15, y: 10, width: 40, height: 40))
        image.image = UIImage(named: "\(skyBalls[row].getImage().split(separator: ".")[0])")
        image.contentMode = .scaleAspectFit
        mainView.addSubview(image)

        let titleLbl = UILabel(frame: CGRect(x: 0, y: 50, width: 70, height: 20))
        titleLbl.text = skyBalls[row].getName()
        titleLbl.textColor = .white
        titleLbl.textAlignment = .center
        titleLbl.adjustsFontSizeToFitWidth = true
        mainView.addSubview(titleLbl)

        mainView.transform = CGAffineTransform(rotationAngle: 90 * CGFloat((Double.pi/180)))
        return mainView
    

    @objc func pickerTapped(tapRecognizer: UITapGestureRecognizer) 
        if tapRecognizer.state == .ended 
            let rowHeight = planetPicker.rowSize(forComponent: 0).height
            let selectedRowFrame = planetPicker.bounds.insetBy(dx: 0, dy: (planetPicker.frame.height - rowHeight) / 2)
            let userTappedOnSelectedRow = selectedRowFrame.contains(tapRecognizer.location(in: planetPicker))
            if userTappedOnSelectedRow 
                let selectedRow = planetPicker.selectedRow(inComponent: 0)
                upgradePlanetName = skyBalls[selectedRow].getName()
                let settings = UIStoryboard(name: "PlanetUpgrade", bundle: .main).instantiateViewController(withIdentifier: "Upgrade") as! UpgradeViewController
                self.present(settings, animated: false)
            
        
    

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool 
        return true
    

我确实会在按下按钮时重新加载 UIPickerView,这会增加 skyBalls 数组的大小:

planetPicker.reloadAllComponents()

但如果 planetPicker.reloadAllComponents() 从项目的所有形式中完全删除,问题仍然存在。

我已尝试重新启动我的 mac、iphone 和 xcode,以及删除派生数据。我清理了构建文件夹,但似乎没有任何问题可以解决。

提前谢谢你。

【问题讨论】:

能否请您突出显示有问题的行?或者确切的问题是什么,请分享更多关于这个问题的信息。 @emrcftci 确定。当应用程序加载选择器视图中的第二个元素时,所有按钮和视图都会冻结。它似乎没有崩溃,但只是在手机变热时坐在那里。我不太确定哪条线路有问题。我试图注释掉 viewForRow 方法并将其替换为标准的数字数组,但问题仍然存在 .getName()函数的细节是什么? @emrcftci 它返回行星名称的字符串。所以第一个索引是“Sun”。 请查看@MoumenAlisawe 的回答 【参考方案1】:

当您必须更新视图时 您必须始终在主线程上调用.reloadAllComponents() DispatchQueue.main.async 应该可以解决问题。

DispatchQueue.main.async 
    self.planetPickerView.reloadAllComponents()

【讨论】:

【参考方案2】:

好吧,我发现这根本不是 UIpickerView 的错。 所以环顾四周后,我尝试了这个:

1) 打开“断点导航器”

2) 点击“+”按钮并选择“异常断点”选项

然后它把我带到了问题的根源,女巫是一个超出范围的数组索引,由于某种原因没有导致应用程序崩溃。

【讨论】:

以上是关于UIPickerView 在重新加载时冻结 UI的主要内容,如果未能解决你的问题,请参考以下文章

UIPickerView 没有重新加载

UIActionSheet 中的 UIPickerView:UI 冻结

UIPickerView 重新加载组件不起作用

重新加载数据时iOS uitableview冻结

当我选择操作栏返回时冻结或制作稳定的 Web 视图?防止每次加载/重新加载

UIPickerView 的 ViewController 在选择后冻结