【问题标题】:Create custom UITableViewCell with using stack view and auto layout使用堆栈视图和自动布局创建自定义 UITableViewCell
【发布时间】:2020-07-06 09:39:08
【问题描述】:

我有 2 个关于约束和自动布局的问题:

  1. 如何以编程方式在下图中创建一个类似单元格的单元格?我不明白如何以编程方式为我的单元格添加自动布局和约束。
  2. 如何为单元格分配默认的 Apple 布局边距? (例如,默认单元格中的左插图等于 20 pt(大屏幕)和 16 pt(小屏幕)。

我当前的代码:

class cellWithTitleAndDetail: UITableViewCell {
    
    // MARK: - Properties
    let title = UILabel()
    let detail = UILabel()
    let stackView = UIStackView()
    

    // MARK: - Override init
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        title.translatesAutoresizingMaskIntoConstraints = false
        detail.translatesAutoresizingMaskIntoConstraints = false
        stackView.translatesAutoresizingMaskIntoConstraints = false
        
        stackView.axis = .horizontal
        stackView.alignment = .center
        stackView.distribution = .fillProportionally
        
        // Set color
        title.textColor = .white
        detail.textColor = .white
        
        // Highlight StackView
        stackView.addBackground(color: .blue)
        
        stackView.addArrangedSubview(title)
        stackView.addArrangedSubview(detail)
        
        stackView.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
        stackView.isLayoutMarginsRelativeArrangement = true

        self.contentView.addSubview(stackView)
                
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }

}

结果:

更新:

下面添加了我的新代码和来自 DonMag 答案的代码。

新问题:“LayoutMarginsGuide”在屏幕宽度等于 375 pt(图 375-1)的 iPhone 上完美运行。但在大尺寸屏幕上,分隔符出现早于单元格(图像 414-2)。我该如何解决这个问题?

新代码:

// MARK: - Override init
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        // Off translatesAutoresizingMaskIntoConstraints
        title.translatesAutoresizingMaskIntoConstraints = false
        detail.translatesAutoresizingMaskIntoConstraints = false
        stackView.translatesAutoresizingMaskIntoConstraints = false
        
        // Setup stackView
        stackView.axis = .horizontal
        stackView.alignment = .center
        stackView.distribution = .fill
        
        // Hugging
        title.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal)
        detail.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal)
        
        // Resistance
        title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal)
        detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .horizontal)
        
        // Set textAlignment
        title.textAlignment = .left
        detail.textAlignment = .right
        
        // Set numberOfLines
        title.numberOfLines = 0
        
        // Highlight stackView and set colors
        stackView.addBackground(color: .blue)
        title.textColor = .white
        detail.textColor = .white
        
        // Add title and detail
        stackView.addArrangedSubview(title)
        stackView.addArrangedSubview(detail)
 
        // Add to subview
        self.contentView.addSubview(stackView)
        
        // Get layoutMarginsGuide
        let layoutMarginsGuide = contentView.layoutMarginsGuide
        
        // Set constraints
        NSLayoutConstraint.activate([
            
            // constrain all 4 sides of the stack view to the content view's layoutMarginsGuide
            stackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor, constant: 0.0),
            stackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: 0.0),
            stackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor, constant: 0.0),
            stackView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor, constant: 0.0),

        ])
                
    }

【问题讨论】:

    标签: swift xcode uitableview autolayout stackview


    【解决方案1】:

    您可以使用内容视图的 layoutMarginsGuide:

        // only if you want different margins than the content view's margins
        //stackView.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
        //stackView.isLayoutMarginsRelativeArrangement = true
        
        self.contentView.addSubview(stackView)
        
        let g = contentView.layoutMarginsGuide
        
        NSLayoutConstraint.activate([
            
            // constrain all 4 sides of the stack view to the
            //  content view's layoutMarginsGuide
            stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
            stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
    
        ])
        
    

    附带说明一下,您真正想要什么并不清楚(如果这不能提供您想要的布局,这将是一个单独的问题)...

    • 您是否希望您的单元格看起来像“列”
    • 您希望“细节”右对齐吗?
    • 细节标签可以是多行的吗?

    编辑

    使用您更新的代码 - 我所做的唯一更改是为标签提供背景颜色,因为您没有显示您的 stackView.addBackground(color: .blue) 代码:

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        // Off translatesAutoresizingMaskIntoConstraints
        title.translatesAutoresizingMaskIntoConstraints = false
        detail.translatesAutoresizingMaskIntoConstraints = false
        stackView.translatesAutoresizingMaskIntoConstraints = false
        
        // Setup stackView
        stackView.axis = .horizontal
        stackView.alignment = .center
        stackView.distribution = .fill
        
        // Hugging
        title.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal)
        detail.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal)
        
        // Resistance
        title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal)
        detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .horizontal)
        
        // Set textAlignment
        title.textAlignment = .left
        detail.textAlignment = .right
        
        // Set numberOfLines
        title.numberOfLines = 0
        
        // Highlight stackView and set colors
        //stackView.addBackground(color: .blue)
        title.backgroundColor = .blue
        detail.backgroundColor = .red
    
        title.textColor = .white
        detail.textColor = .white
        
        // Add title and detail
        stackView.addArrangedSubview(title)
        stackView.addArrangedSubview(detail)
    
        // Add to subview
        self.contentView.addSubview(stackView)
        
        // Get layoutMarginsGuide
        let layoutMarginsGuide = contentView.layoutMarginsGuide
        
        // Set constraints
        NSLayoutConstraint.activate([
            
            // constrain all 4 sides of the stack view to the content view's layoutMarginsGuide
            stackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor, constant: 0.0),
            stackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: 0.0),
            stackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor, constant: 0.0),
            stackView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor, constant: 0.0),
    
        ])
                
    }
    

    这是我得到的:


    编辑 2

    表格视图单元格分隔符可以根据设备、iOS 版本、表格视图样式等变化。

    为获得最可靠的一致性,请自行设置。

    这是一个例子......

    • 我们将单元格的 stackView 约束相对于 contentViewnot 设置为内容视图的布局边距指南。
    • 我们设置了表格视图的separatorInset,因此左侧插图与堆栈视图的前导锚点匹配。
    • 我们还需要将每个单元格的.separatorInset 设置为我们表格视图的自定义.separatorInset

    这是完整的代码:

    class MyTestTableViewController: UITableViewController {
        
        let testTitles: [String] = [
            "Yesterday all my troubles seemed so far away, Now it looks as though they're here to stay.",
            "She packed my bags last night pre-flight, Zero hour nine AM.",
            "When you're weary, feeling small, When tears are in your eyes, I will dry them all."
        ]
        let testDetails: [String] = [
            "The Beatles",
            "Elton John",
            "Simon & Garfunkel",
        ]
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            tableView.register(MyCellWithTitleAndDetail.self, forCellReuseIdentifier: "myCell")
            
            // our custom separatorInset
            //  left matches cell's stackView leading anchor
            tableView.separatorInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 0)
        }
        
        override func numberOfSections(in tableView: UITableView) -> Int {
            return 3
        }
        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return 3
        }
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            
            let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyCellWithTitleAndDetail
                
            // set cell separatorInset equal to tableView separatorInset
            cell.separatorInset = tableView.separatorInset
                
            cell.title.text = testTitles[indexPath.row]
            cell.detail.text = testDetails[indexPath.row]
            
            return cell
    
        }
    
    }
    
    class MyCellWithTitleAndDetail: UITableViewCell {
        
        // MARK: - Properties
        let title = UILabel()
        let detail = UILabel()
        let stackView = UIStackView()
        
        
        // MARK: - Override init
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
    
            // Off translatesAutoresizingMaskIntoConstraints
            title.translatesAutoresizingMaskIntoConstraints = false
            detail.translatesAutoresizingMaskIntoConstraints = false
            stackView.translatesAutoresizingMaskIntoConstraints = false
            
            // Setup stackView
            stackView.axis = .horizontal
            stackView.alignment = .center
            
            stackView.distribution = .fillEqually
    
            // if we want the labels to be 50% of the width,
            //  set stackView.distribution = .fillEqually
            //  then we don't need any Content Hugging or Compression Resistance priority changes
            
    //      // Hugging
    //      title.setContentHuggingPriority(UILayoutPriority(rawValue: 750), for: .horizontal)
    //      detail.setContentHuggingPriority(UILayoutPriority(rawValue: 250), for: .horizontal)
    //
    //      // Resistance
    //      title.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 750), for: .horizontal)
    //      detail.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 250), for: .horizontal)
    
            // Set textAlignment
            title.textAlignment = .left
            detail.textAlignment = .right
            
            // Set numberOfLines
            title.numberOfLines = 0
            
            // Highlight stackView and set colors
            
            title.backgroundColor = .blue
            detail.backgroundColor = .red
            //stackView.addBackground(color: .blue)
            
            title.textColor = .white
            detail.textColor = .white
            
            // Add title and detail
            stackView.addArrangedSubview(title)
            stackView.addArrangedSubview(detail)
            
            // Add to subview
            self.contentView.addSubview(stackView)
            
            // Set constraints
            NSLayoutConstraint.activate([
                
                // constrain all 4 sides of the stack view to the content view
                //  with your own "margins"
                stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12.0),
                stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15.0),
                stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15.0),
                stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -12.0),
                
            ])
            
        }
        
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
    }
    

    结果:

    【讨论】:

    • @Bandyliuk - 得到您问题的答案,然后用新问题编辑您的问题没有帮助。如果您得到有帮助的答案,请接受...如果您有 问题,请发新帖。也就是说,请参阅我的答案的编辑。一定是您正在做的其他事情导致与分隔符不对齐。
    • 对不起,我一开始没有点击“接受答案”。感谢您的帮助。
    • 您可以将表格样式更改为“UITableView.Style.insetGrouped”并再次尝试运行代码吗?因为使用“plain”样式我的代码运行良好,但使用“insetGrouped” - 不。
    • @Bandyliuk - 供将来参考:当您在 Stack Overflow 上发帖时,请尝试包含 所有 信息。请参阅我的答案底部的 Edit 2
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-21
    • 2019-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多