collectionview 或 uitableview 内的 Stackview

Posted

技术标签:

【中文标题】collectionview 或 uitableview 内的 Stackview【英文标题】:Stackview inside collectionview or uitableview 【发布时间】:2020-11-08 13:53:59 【问题描述】:

我正在尝试实现文本的布局,然后是图像(基于纵横比计算的图像高度),然后是文本等等。问题是我将视图添加到随机压缩视图的堆栈视图有时图像视图会在文本中消失一段时间,它没有一致的行为。

我在 uitableview 和 uicolletion 视图上都试过了,结果是一样的。是否将上述观点的组合视为此类用例的最佳实践?如果不是这样的话,最好的做法是什么?

class MyStackyView: UIStackView 

// Main variables
weak var videoPlayerDelegate: AVPlayerViewDelegate?
private var avVideoPlayersVC: [AVPlayerViewController] = []
var content: [Content]! 
    didSet 
        contentCombined = Utility.shared.combineToNew(contents: content)
    

private var contentCombined: [Content] = [] 
    didSet 
        populatePostContent()
    

var contentViews: [UIView] = []     // Holds the views created


override init(frame: CGRect) 
    super.init(frame: frame)
    configureView()


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


deinit 
    print("DiaryPostView:: Deinitalized")


private func configureView() 
    axis = .vertical
    distribution = .fill
    alignment = .fill
    spacing = 0

// 用于填充帖子内容的扩展 扩展 MyStackyView

private func populatePostContent() 
    
    for content in contentCombined 
        if content.isMedia 
            addMedia(content)
         else 
            addText(content.text)
        
        
    
    

// 添加所需视图的扩展 扩展 MyStackyView

private func addText(_ text: String?, place: MediaPlace = .center) 
    
    let textView = generateDefaultTextView()
    //let parsedText = htmlParser.shared.parseHTMLToAttributed(string: text ?? "") // fix font issue

    switch place 
        
        case .center:
            append(textView)
            contentViews.append(textView)
            
    
    
    textView.text = text

        // لما استخدم ال parsedtext مرة النص بطلع مع الfont و مرة لا



private func addMedia(_ content: Content) 
    
    let avPlayerVC = getAVPlayerViewController()
    let mediaView = generateDefaultMediaView()
    
    switch content.getRawPlace() 
        case .center:
            append(mediaView)
            contentViews.append(mediaView)
            addText(content.text)
            NetworkManager().downloadMedia(content.img!, into: mediaView, avPlayerViewController: avPlayerVC) 
                
            
            
        
            
    
       
    

扩展 MyStackyView

private func generateDefaultTextView() -> UILabel 
    let textView = UILabel()
    textView.backgroundColor = .clear
            
    textView.numberOfLines = 0
    textView.font = UIFont.customFont(.openSans, .regular, .title1, 17)

    return textView


private func generateDefaultHorizontalStack() -> UIStackView 
    let horizontalStack = UIStackView()
    horizontalStack.axis = .horizontal
    horizontalStack.distribution = .fill
    horizontalStack.alignment = .fill
    return horizontalStack


private func generateDefaultMediaView() -> MediaSliderView 
    let mediaSliderView = MediaSliderView()
    return mediaSliderView


private func getAVPlayerViewController() -> AVPlayerViewController? 
    videoPlayerDelegate?.getAVPlayerVC?()


func deallocateAVPlayers() 
    for player in avVideoPlayersVC 
        player.removeFromParent()
    
    avVideoPlayersVC.removeAll()

我在我的 uitableviewcell 中初始化该类的一个变量,然后添加这些约束

contentView.addSubview(MyStackyView)
    MyStackyView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8).isActive = true
    MyStackyView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8).isActive = true
    MyStackyView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16).isActive = true
    MyStackyView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16).isActive = true

如果可能的话,我需要一些关于这个问题的指导。

谢谢你的帮助

【问题讨论】:

欢迎来到 ***。请使用tour 并查看How to Ask。您需要提供有关您正在做什么的更多信息......这是垂直或水平堆栈视图吗?你是如何设置约束的?您是否将您的单元设计为故事板原型?如果是这样,请显示该布局。如果您是通过代码执行此操作,请显示您的代码。 @DonMag 感谢您的回复,您可以看到这是我第一次在 *** 上提问。现在回答你的问题,它是一个垂直堆栈视图,现在约束只是根据我的纵横比计算为我的图像视图设置的高度约束,并且标签设置为 numberOfLines 为 0。现在以编程方式创建视图,我在帖子中附上了代码作为参考点。非常感谢。 @DonMag 我自己使用了一个滚动视图,里面有这个stackview,它显示得非常好,当我把stackview放在uitableviewcell或uicollectionviewcell中时会发生这个问题。 您的代码中有很多是在其他地方定义的(所以我们不知道它是什么),而且您现在有很多事情要做。我建议从更简单的开始...创建一个带有垂直堆栈视图的单元格,其中包含几个元素(例如UILabelUIImageView)并让它工作。然后开始添加您的其他元素并将代码拆分为扩展。事实上,太多的未知数。 @DonMag 太好了,所以我将从一个简单的 stackview 开始,它有一个 text 和 imageview 并从那里开始。谢谢,会及时更新。希望一切顺利,谢谢。 【参考方案1】:

这是一个(相当)基本的例子。

我们将使用这样的数据结构:

struct VarDevStruct 
    var first: String = ""
    var second: String = ""
    var imageName: String = ""

cell 类有一个垂直堆栈视图,其中包含:

多行标签 水平堆栈视图 具有 80x80 图像视图和标签 多行标签

如果数据结构中的任何元素是空字符串,我们会将单元格中的相应元素设置为隐藏

首先,结果:

向下滚动到包含不同数据的几行后:

并旋转:

这是完整的代码...里面有很多cmets,所以应该清楚代码在做什么。


数据结构

struct VarDevStruct 
    var first: String = ""
    var second: String = ""
    var imageName: String = ""


细胞类

class VarDevCell: UITableViewCell 
    
    let firstLabel = UILabel()
    let secondLabel = UILabel()
    let imgView = UIImageView()
    let imgNameLabel = UILabel()
    let vStack = UIStackView()
    let hStack = UIStackView()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    
    required init?(coder: NSCoder) 
        super.init(coder: coder)
        commonInit()
    
    func commonInit() -> Void 
        
        // stack view properties
        vStack.axis = .vertical
        vStack.alignment = .fill
        vStack.distribution = .fill
        vStack.spacing = 8
        
        vStack.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(vStack)
        
        // let's use the default cell margins
        let g = contentView.layoutMarginsGuide
        
        NSLayoutConstraint.activate([
            // constrain stack view to all 4 sides
            vStack.topAnchor.constraint(equalTo: g.topAnchor),
            vStack.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            vStack.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            vStack.bottomAnchor.constraint(equalTo: g.bottomAnchor),
        ])
        
        // subview properties
        
        // background colors to make it easy to see the frames
        firstLabel.backgroundColor = .yellow
        secondLabel.backgroundColor = .green
        imgView.backgroundColor = .red
        imgNameLabel.backgroundColor = .cyan
        
        // multi-line labels
        firstLabel.numberOfLines = 0
        secondLabel.numberOfLines = 0
        
        imgNameLabel.textAlignment = .center
        
        // image view defaults to scaleToFill
        //  let's set it to scaleAspectFit
        imgView.contentMode = .scaleAspectFit
        
        // horizontal stack view
        hStack.axis = .horizontal
        hStack.alignment = .center
        hStack.distribution = .fill
        hStack.spacing = 8

        // add subviews to horizontal stack view
        hStack.addArrangedSubview(imgView)
        hStack.addArrangedSubview(imgNameLabel)
        
        // let's fill the vertical stack view with
        //  label
        //  hStack with 80x80 imageview and label with image name
        //  label
        vStack.addArrangedSubview(firstLabel)
        vStack.addArrangedSubview(hStack)
        vStack.addArrangedSubview(secondLabel)
        
        // set image view width and height
        imgView.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
        imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: 1.0).isActive = true

    
    
    func fillData(_ vdStruct: VarDevStruct) -> Void 
        firstLabel.text = vdStruct.first
        secondLabel.text = vdStruct.second
        imgNameLabel.text = vdStruct.imageName
        
        // does our data have an image name?
        if !vdStruct.imageName.isEmpty 
            if #available(ios 13.0, *) 
                if let img = UIImage(systemName: vdStruct.imageName) 
                    imgView.image = img
                
             else 
                // Fallback on earlier versions
                if let img = UIImage(named: vdStruct.imageName) 
                    imgView.image = img
                
            
        
        
        // hide elements that we don't need in this cell
        firstLabel.isHidden = vdStruct.first.isEmpty
        secondLabel.isHidden = vdStruct.second.isEmpty
        hStack.isHidden = vdStruct.imageName.isEmpty
    
    


控制器类

class VarDevTableViewController: UITableViewController 
    
    var myData: [VarDevStruct] = []
    
    override func viewDidLoad() 
        super.viewDidLoad()
        
        // register cell class for reuse
        tableView.register(VarDevCell.self, forCellReuseIdentifier: "cell")
        
        // generate some sample data
        myData = makeSampleData()
    
    
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) 
        super.viewWillTransition(to: size, with: coordinator)

        coordinator.animate(alongsideTransition: nil, completion: 
            _ in
            // make sure table re-calculates row heights
            UIView.setAnimationsEnabled(false)
            self.tableView.performBatchUpdates(nil, completion: nil)
            UIView.setAnimationsEnabled(true)
        )

    

    override func numberOfSections(in tableView: UITableView) -> Int 
        return 1
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return myData.count
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! VarDevCell
        cell.fillData(myData[indexPath.row])
        return cell
    
    
    func makeSampleData() -> [VarDevStruct] 
        var a: [VarDevStruct] = []
        
        // 15 sample data elements
        for i in 1...15 
            let d = VarDevStruct(first: "This is the text for the first label in row: \(i).",
                                 second: "This will be a longer string to be used as the text for the second label in row \(i) (long enough to make sure we're getting some word wrapping).",
                                 imageName: "\(i).square.fill")
            a.append(d)
        

        // change some of the sample data for variations
        //  (arrays are zero-based)
        
        // fifth row: no first label
        a[4].first = ""
        a[4].second = "This row has no First label text."
        
        // sixth row: no image
        a[5].first = "This row has no image."
        a[5].imageName = ""
        
        // seventh row: no second label
        a[6].first = "This row has no second label."
        a[6].second = ""
        
        // eigth row: no image or second label
        a[7].first = "This row has no image, and has no second label. The next row (9) has image only."
        a[7].imageName = ""
        a[7].second = ""
        
        // ninth row: image only
        a[8].first = ""
        a[8].second = ""
        
        // tenth row: first label with mutliple lines
        a[9].first = "One\nTwo\nThree\nFour"
        a[9].second = "This row has embedded newline chars in the text of the first label."

        return a
    
    

【讨论】:

太好了,我开始工作了,一切都很好,非常感谢。我正在做的是添加标签,然后当我有图像时,我在内部附加了一个带有图像视图的 UIView,在从网络调用加载图像后,我更新了该视图的约束(问题就在这里,这就是它的原因挤压其他标签),非常感谢您的帮助,非常感谢。

以上是关于collectionview 或 uitableview 内的 Stackview的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionView insertItemsAtIndexPaths:似乎重新加载collectionview或自动滚动到顶部

如何在 CollectionView 中显示从照片库或相机中选择的图像?画廊视图

在单元格标签等于给定字符串的collectionView或TableView上选择行/部分?

如何在巨大的 CollectionView 中使用 SDWebImage 或 AFNetworking 在磁盘上缓存图像?

如何居中对齐 CollectionView 单元格?

iOS - 在视图(Swift)中为多个Tableview或Collectionviews使用/传递手势识别器