swift - 快速按下集合视图单元格时如何修复自定义视图中的约束

Posted

技术标签:

【中文标题】swift - 快速按下集合视图单元格时如何修复自定义视图中的约束【英文标题】:How to fix constraints in custom view when collection view cell pressed in swift 【发布时间】:2019-12-02 23:22:55 【问题描述】:

我有一个测试应用程序,我第一次尝试以编程方式创建 100%。我有一个简单的 collectionView,每个单元格中都有一个图像和一个标签。

当我按下单元格时,会弹出一个自定义视图,其中有两个视图,红色和蓝色,将来会有内容,但现在只有两个视图。

我想让该应用程序在纵向和横向方向上都可以运行,并且在开始时它运行良好。我第一次按下单元格时,弹出视图适用于纵向和横向。

这是我想要在任何时候按下单元​​格时发生的事情,但是当我点击视图使其消失时出现问题,然后按下任何单元格:

第一个方向,无论是横向还是纵向,都可以,但是当我现在改变方向时,第二个或更多单元格被按下时,问题就出现了。

我尝试使用首次亮相的视图层次结构,这就是错误屏幕的样子。

我遵循的主要思想是该站点的教程: https://iosrevisited.blogspot.com/2017/10/adaptive-autolayout-programmatically-iphone-x.html

我能够完成本教程,并且效果很好。我所拥有的不同之处在于,我的视图应该在用户想要时出现和消失。

这是具有 collectionView 的视图控制器的代码:

import UIKit

extension ViewController: UICollectionViewDelegateFlowLayout 

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
        return CGSize(width: 100, height: 150)
    

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets 
        return UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
    



extension ViewController: UICollectionViewDataSource 
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
        return items.count
    

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! TestingCollectionViewCell
        cell.backgroundColor = .white
        let img = UIImage(named: self.items[indexPath.row])
        cell.imageView.image = img
        cell.imageName.text = "\(self.items[indexPath.row])"
        return cell
    


    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
        print("\(items[indexPath.row])")
        self.presentInfoView(withInfo: items[indexPath.row])
    


class ViewController: UIViewController, BlurDelegate 

    func removeBlurView() 
        for subview in view.subviews 
            if subview.isKind(of: UIVisualEffectView.self) 
                subview.removeFromSuperview()
                self.infoView.removeFromSuperview()
            
        
    

    let infoView: InfoView = 
       let view = InfoView()
        view.layer.cornerRadius = 5
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    ()

    fileprivate var items: [String] = [
        "photo1",
        "photo2",
        "photo3",
        "photo4",
        "photo5",
        "photo6",
        "photo7",
        "photo8",
        "photo9",
        "photo10",
        "photo11",
        "photo12",
        "photo13",
        "photo14",
        "photo15",
        "photo16",
        "photo17",
        "photo18",
        "photo19",
        "photo20",
        "photo21",
        "photo22",
        "photo23",
        "photo24"
    ]

    override func viewDidLoad() 
        super.viewDidLoad()

        let collection = UICollectionView(frame: view.frame, collectionViewLayout: UICollectionViewFlowLayout())
                //allows us to use auto layout constraints
        collection.translatesAutoresizingMaskIntoConstraints = false
        collection.backgroundColor = .black
        view.addSubview(collection)
        collection.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        collection.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        collection.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        collection.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true

        collection.dataSource = self
        collection.delegate = self

        collection.register(TestingCollectionViewCell.self, forCellWithReuseIdentifier: "cell")
        self.navigationItem.title = "Testing"

    

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 
        var touch: UITouch? = touches.first
        if touch?.view != infoView 
            dismissView()

        
    


    func dismissView() 

        //dismiss(animated: true, completion: nil)
        removeBlurView()

    

    func setBlurView() 
        let blurView = UIVisualEffectView()
        blurView.frame = view.frame
        blurView.effect = UIBlurEffect(style: .regular)
        blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(blurView)
    

    func presentInfoView(withInfo info: String) 
        setBlurView()
        view.addSubview(infoView)
        infoView.addView()
        let img = UIImage(named: info)
        infoView.imageView.image = img
        infoView.nameLbl.text = info


        infoView.backgroundColor = .white
        infoView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        infoView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8).isActive = true
        //infoView.widthAnchor.constraint(equalToConstant: view.frame.width - 64).isActive = true
        infoView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.8).isActive = true
        //infoView.heightAnchor.constraint(equalToConstant: view.frame.height - 64).isActive = true
        infoView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -44).isActive = true
        infoView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)

        infoView.alpha = 0

        UIView.animate(withDuration: 0.5) 
            self.infoView.alpha = 1
            self.infoView.transform = .identity
        
    


为了清楚起见,我们试图做的是让模糊视图出现并随着视图的出现而消失。

这是当单元格出现时将出现的视图代码:

import UIKit
protocol BlurDelegate: class 
    func removeBlurView()

class InfoView: UIView 


    let topView: UIView = 
       let view = UIView()
        view.backgroundColor = .blue
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    ()

    let btmView: UIView = 
       let view = UIView()
        view.backgroundColor = .red
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    ()


    var portraitConstraints: [NSLayoutConstraint] = []
    var landscapeConstraints: [NSLayoutConstraint] = []

    override init(frame: CGRect) 
        super.init(frame: frame)
        NotificationCenter.default.addObserver(self, selector: #selector(toggleConstraints), name: UIDevice.orientationDidChangeNotification, object: nil)
        //addView()
    

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

    func addView() 
        let guide = self.safeAreaLayoutGuide

        //image layout
        self.addSubview(topView)
        let defaultImgTop = topView.topAnchor.constraint(equalTo: guide.topAnchor)
        let defaultImgLeading = topView.leadingAnchor.constraint(equalTo: guide.leadingAnchor)

        //portrait
        let portraitImgTrailing = topView.trailingAnchor.constraint(equalTo: guide.trailingAnchor)
        let portraitImgHeight = topView.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 0.5)


        //lbl layouts
        self.addSubview(btmView)
        let defaultlblTrailing = btmView.trailingAnchor.constraint(equalTo: guide.trailingAnchor)
        let defaultLblBottom = btmView.bottomAnchor.constraint(equalTo: guide.bottomAnchor)

        //portrait
        let portraitLblBottom = btmView.topAnchor.constraint(equalTo: topView.bottomAnchor)
        let portraitLblLeading = btmView.leadingAnchor.constraint(equalTo: guide.leadingAnchor)

        //imageView landscape constraints
        let landscapeImgBottom = topView.bottomAnchor.constraint(equalTo: guide.bottomAnchor)
        let landscapeImgWidth = topView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.5)


        //label landscape constraints
        let landscapeLblTop = btmView.topAnchor.constraint(equalTo: guide.topAnchor)
        let landscapeLblLeading = btmView.leadingAnchor.constraint(equalTo: topView.trailingAnchor)

        let defaultConstriants = [defaultImgTop, defaultImgLeading, defaultLblBottom, defaultlblTrailing]
        portraitConstraints = [portraitImgHeight, portraitImgTrailing, portraitLblBottom, portraitLblLeading]
        landscapeConstraints = [landscapeImgBottom, landscapeLblTop, landscapeLblLeading, landscapeImgWidth]
        self.addConstraints(defaultConstriants)
        toggleConstraints()
    



    @objc func toggleConstraints() 
        if UIDevice.current.orientation.isLandscape 
            self.removeConstraints(portraitConstraints)
            self.addConstraints(landscapeConstraints)
         else 
            self.removeConstraints(landscapeConstraints)
            self.addConstraints(portraitConstraints)
        
    


我从上面添加的网站中获得了这个视图中的想法。我不得不改变一些东西,因为它是一个视图,但它应该是一样的。我认为错误就在于此,但我尝试更改纵向和横向的约束,但没有奏效。我试着把它写下来,看看哪些约束被删除然后再次更新,但我似乎无法找到是哪个约束让我感到困惑。 我知道问题应该出在红色视图的顶部锚点或蓝色视图的底部锚点,但我无法确定哪个有问题或错误。

我希望这是足够的信息,但是请询问我是否还有其他可以提供帮助的信息。

另外,如果不是很麻烦,如果您注意到视图出现时,导航栏似乎没有模糊。这使它看起来像自定义视图在顶部被切断。如果您知道一种模糊导航栏的方法,或者尝试将视图的顶部锚点向下推以使其看起来不那么糟糕,我们将不胜感激。

我试过用

self.navigationItem.bottomAnchor

我尝试在视图中添加一个带有常量的顶部锚点,但我上面的配置是我能找到的让视图每次都完美显示的最佳方式。 非常感谢

【问题讨论】:

【参考方案1】:

presentInfoView 函数删除 infoView.addView()

     override init(frame: CGRect) 
                super.init(frame: frame)
                NotificationCenter.default.addObserver(self, selector: #selector(toggleConstraints), name: UIDevice.orientationDidChangeNotification, object: nil)
                addView()

【讨论】:

以上是关于swift - 快速按下集合视图单元格时如何修复自定义视图中的约束的主要内容,如果未能解决你的问题,请参考以下文章

Swift UI 集合视图布局

在 SWIFT 的表视图中填充自定义单元格时出错

重用自定义 tableView 单元格时重复显示相同的子视图(Swift 4.1)

保存在 UIImageView 中显示的图像

swift - 在UITableView中选择单元格时如何更改按钮名称?

如何在使用 tableViews 自调整单元格时在运行时更改表格视图单元格高度?