如何固定约束间距?

b09cbbtk  于 2022-09-19  发布在  Swift
关注(0)|答案(1)|浏览(147)

我正在尝试创建如下图所示的语法视图。然而,一旦我将三个水平堆栈视图嵌入到垂直堆栈视图中,整个事情就分散了。我已经设置了间距和对齐方式,但由于某种原因,当我在playgroung中运行它时,它不能正确地显示视图;间距变得混乱。我如何解决此问题?请忽略铅笔图像。它只是一个占位符

import UIKit
import SnapKit

import Foundation
import PlaygroundSupport

extension UIStackView {

  func removeAllSubviews() {
    arrangedSubviews.forEach { B1a0a1b.removeFromSuperview() }
  }

  convenience init(frame: CGRect = .zero,
           alignment: UIStackView.Alignment = .fill,
           axis: NSLayoutConstraint.Axis = .vertical,
           distribution: UIStackView.Distribution = .equalSpacing,
           spacing: CGFloat = 0.0) {

    self.init(frame: frame)

    self.alignment = alignment
    self.axis = axis
    self.distribution = distribution
    self.spacing = spacing
  }
}

class UserCell: UIView {

  private lazy var reputationLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.font = UIFont.systemFont(ofSize: 15, weight: .bold)
    label.text = "Reputation"
    return label
  }()

  private lazy var increasePerformaceLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.font = UIFont.systemFont(ofSize: 10, weight: .semibold)
    label.text = "Increase Performance"
    return label
  }()

  private lazy var increasePerformaceImage : UIImageView = {
    let imageView = UIImageView()
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.image = UIImage(systemName: "pencil.circle.fill")
    return imageView
  }()

  private lazy var reviewsImage : UIImageView = {
    let imageView = UIImageView()
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.image = UIImage(systemName: "pencil.circle.fill")
    return imageView
  }()

  private lazy var reviewsLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.font = UIFont.systemFont(ofSize: 13, weight: .bold)
    label.text = "Reviews"
    return label

  }()
  private lazy var dateLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.font = UIFont.systemFont(ofSize: 13, weight: .bold)
    label.text = "All Time as of 2020-10-10"
    return label

  }()

  private lazy var scoreLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.font = UIFont.systemFont(ofSize: 15, weight: .semibold)
    label.textColor = .darkGray
    label.text = "325"
    return label
  }()

  private lazy var ratingsLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
    label.textColor = .darkGray
    label.text = "Ratings"
    return label
  }()

  private lazy var ratingsNumberLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
    label.textColor = .darkGray
    label.text = "4.5"
    return label
  }()

  private lazy var ratingStarImage : UIImageView = {
    let imageView = UIImageView()
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.image = UIImage(systemName: "pencil.circle.fill")
    return imageView
  }()

  override init(frame: CGRect) {
    super.init(frame: frame)
    addComponents()
  }

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

  }

  private func addComponents() {

    let reputationStackView = UIStackView(frame: .zero, alignment: .top, axis: .horizontal, distribution: .equalCentering, spacing:25)

    let reviewStackView = UIStackView(frame: .zero, alignment: .top, axis: .horizontal, distribution: .equalCentering, spacing: 25)

    let ratingStackView = UIStackView(frame: .zero, alignment: .top, axis: .horizontal, distribution: .equalSpacing, spacing: 20)

    let dateStackView = UIStackView(frame: .zero, alignment: .top, axis: .horizontal, distribution: .equalSpacing, spacing: 20)

    let verticalStackView = UIStackView(frame: .zero, alignment: .top, axis: .vertical, distribution: .fill, spacing: 9)

    reputationStackView.addArrangedSubview(reputationLabel)
    reputationStackView.addArrangedSubview(increasePerformaceLabel)
    reputationStackView.addArrangedSubview(increasePerformaceImage)

    reviewStackView.addArrangedSubview(reviewsImage)
    reviewStackView.addArrangedSubview(reviewsLabel)
    reviewStackView.addArrangedSubview(scoreLabel)

    dateStackView.addArrangedSubview(dateLabel)

    ratingStackView.addArrangedSubview(ratingsLabel)
    ratingStackView.addArrangedSubview(ratingsNumberLabel)
    ratingStackView.addArrangedSubview(ratingStarImage)

    verticalStackView.addArrangedSubview(reputationStackView)
    verticalStackView.addArrangedSubview(reviewStackView)
    verticalStackView.addArrangedSubview(dateStackView)
    verticalStackView.addArrangedSubview(ratingStackView)

    addSubview(verticalStackView)

    increasePerformaceImage.snp.makeConstraints { (make) in
      make.width.height.equalTo(20)
    }
    reviewsImage.snp.makeConstraints { (make) in
      make.width.height.equalTo(20)
    }
    ratingStarImage.snp.makeConstraints { (make) in
      make.width.height.equalTo(20)
    }

    verticalStackView.snp.makeConstraints { (make) in
          make.edges.equalToSuperview()
        }
  }
}

let userCell = UserCell(frame: CGRect(x: 0, y: 0, width: 400, height: 100))
PlaygroundPage.current.liveView = userCell
rmbxnbpk

rmbxnbpk1#

正如我在评论中提到的..。在布局开发过程中,为UI元素提供对比背景色以便于在运行时看到它们的框架是非常有帮助的。

此外,它*真的、真的、真的有助于在代码中添加注解,因此您知道预期**会发生什么。

看一下对UserCell类的修改:

class UserCell: UIView {

    private lazy var reputationLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 15, weight: .bold)
        label.text = "Reputation"
        return label
    }()

    private lazy var increasePerformaceLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 10, weight: .semibold)
        label.textColor = .gray
        label.text = "Increased Performance"
        return label
    }()

    private lazy var increasePerformaceImage : UIImageView = {
        let imageView = UIImageView()
        imageView.image = UIImage(systemName: "pencil.circle.fill")
        return imageView
    }()

    private lazy var reviewsImage : UIImageView = {
        let imageView = UIImageView()
        imageView.image = UIImage(systemName: "pencil.circle.fill")
        return imageView
    }()

    private lazy var reviewsLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 13, weight: .bold)
        label.text = "Reviews"
        return label
    }()

    private lazy var dateLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 13, weight: .bold)
        label.textColor = .gray
        label.text = "All Time as of 2020-10-10"
        return label
    }()

    private lazy var scoreLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 28, weight: .semibold)
        label.textColor = .darkGray
        label.text = "325"
        return label
    }()

    private lazy var ratingsLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
        label.textColor = .darkGray
        label.text = "Rating"
        return label
    }()

    private lazy var ratingsNumberLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
        label.textColor = .darkGray
        label.text = "4.5"
        return label
    }()

    private var colorBackgrounds: Bool = false
    convenience init(debug: Bool) {
        self.init(frame: .zero)
        self.colorBackgrounds = debug
        addComponents()
    }

    private func addComponents() {

        // horizontal stack view for the "top row" - alignment is Vertical Centering
        let repStack = UIStackView(frame: .zero, alignment: .center, axis: .horizontal, distribution: .fill, spacing: 8.0)
        repStack.addArrangedSubview(reputationLabel)
        repStack.addArrangedSubview(increasePerformaceLabel)
        repStack.addArrangedSubview(increasePerformaceImage)

        // increasePerformaceLabel needs right alignment
        increasePerformaceLabel.textAlignment = .right

        // increasePerformaceImage 20x20
        increasePerformaceImage.snp.makeConstraints { (make) in
            make.width.height.equalTo(20.0)
        }

        // horizontal stack view for the "middle row" - alignment is Vertical Centering
        let revStack = UIStackView(frame: .zero, alignment: .center, axis: .horizontal, distribution: .fill, spacing: 8.0)
        // we'll use a vertical "sub stack" for the left-side of this "row" - alignment is Leading
        let revLeftStack = UIStackView(frame: .zero, alignment: .leading, axis: .vertical, distribution: .fill, spacing: 2.0)
        // we'll use a horizontal "sub stack" for the "top line" of the "left side" - alignment is Vertical Centering
        let revLeftTopStack = UIStackView(frame: .zero, alignment: .center, axis: .horizontal, distribution: .fill, spacing: 4.0)

        revLeftTopStack.addArrangedSubview(reviewsImage)
        revLeftTopStack.addArrangedSubview(reviewsLabel)

        revLeftStack.addArrangedSubview(revLeftTopStack)
        revLeftStack.addArrangedSubview(dateLabel)

        revStack.addArrangedSubview(revLeftStack)
        revStack.addArrangedSubview(scoreLabel)

        // reviewsImage 20x20
        reviewsImage.snp.makeConstraints { (make) in
            make.width.height.equalTo(20.0)
        }

        // horizontal stack view for the "bottom row" - alignment is Vertical Centering
        let ratStack = UIStackView(frame: .zero, alignment: .center, axis: .horizontal, distribution: .fill, spacing: 8.0)
        // horizontal stack view for the stars - alignment is Vertical Centering
        let starStack = UIStackView(frame: .zero, alignment: .center, axis: .horizontal, distribution: .fill, spacing: 2.0)

        ratStack.addArrangedSubview(ratingsLabel)
        ratStack.addArrangedSubview(ratingsNumberLabel)
        ratStack.addArrangedSubview(starStack)

        // just for example, 4 filled stars and 1 half-filled star
        for _ in 1...4 {
            if let img = UIImage(systemName: "star.fill") {
                let v = UIImageView(image: img)
                v.snp.makeConstraints { (make) in
                    make.height.equalTo(16.0)
                    make.width.equalTo(v.snp.height).multipliedBy(1.2)
                }
                starStack.addArrangedSubview(v)
            }
        }
        for _ in 5...5 {
            if let img = UIImage(systemName: "star.lefthalf.fill") {
                let v = UIImageView(image: img)
                v.snp.makeConstraints { (make) in
                    make.height.equalTo(16.0)
                    make.width.equalTo(v.snp.height).multipliedBy(1.2)
                }
                starStack.addArrangedSubview(v)
            }
        }

        // we want two "separator" lines
        let sepLineViewA = UIView()
        sepLineViewA.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
        let sepLineViewB = UIView()
        sepLineViewB.backgroundColor = sepLineViewA.backgroundColor

        // vertical stack view to hold the "rows" and sep lines
        let vs = UIStackView(frame: .zero, alignment: .center, axis: .vertical, distribution: .fill, spacing: 8.0)

        vs.addArrangedSubview(repStack)
        vs.addArrangedSubview(sepLineViewA)
        vs.addArrangedSubview(revStack)
        vs.addArrangedSubview(sepLineViewB)
        vs.addArrangedSubview(ratStack)

        // add vertical stack view to self
        addSubview(vs)

        // let's add a little top and bottom "padding"
        //  and fill the width
        vs.snp.makeConstraints { (make) in
            make.top.bottom.equalToSuperview().inset(8.0)
            make.leading.trailing.equalToSuperview()
        }

        // set widths for "row" stack views and sep line views
        repStack.snp.makeConstraints { (make) in
            make.width.equalToSuperview().inset(16.0)
        }
        // top separator line extends full width
        sepLineViewA.snp.makeConstraints { (make) in
            make.width.equalToSuperview()
        }
        revStack.snp.makeConstraints { (make) in
            make.width.equalTo(repStack.snp.width)
        }
        sepLineViewB.snp.makeConstraints { (make) in
            make.width.equalTo(repStack.snp.width)
        }
        ratStack.snp.makeConstraints { (make) in
            make.width.equalTo(repStack.snp.width)
        }

        // "row" stack views and sep line views heights
        //  Note: we do NOT set a height for the Reputation stack view

        // this will make all three stack views the same heights
        revStack.snp.makeConstraints { (make) in
            make.height.equalTo(repStack.snp.height)
        }
        ratStack.snp.makeConstraints { (make) in
            make.height.equalTo(repStack.snp.height)
        }

        // sep line views heights
        sepLineViewA.snp.makeConstraints { (make) in
            make.height.equalTo(1.0)
        }
        sepLineViewB.snp.makeConstraints { (make) in
            make.height.equalTo(1.0)
        }

        if colorBackgrounds {
            repStack.backgroundColor = UIColor(red: 1.0, green: 0.9, blue: 0.9, alpha: 1.0)
            revStack.backgroundColor = UIColor(red: 0.9, green: 1.0, blue: 0.9, alpha: 1.0)
            ratStack.backgroundColor = UIColor(red: 0.9, green: 0.9, blue: 1.0, alpha: 1.0)

            reputationLabel.backgroundColor = .yellow
            increasePerformaceLabel.backgroundColor = .cyan
            reviewsLabel.backgroundColor = .green
            dateLabel.backgroundColor = .yellow
            scoreLabel.backgroundColor = .cyan
            ratingsLabel.backgroundColor = .green
            ratingsNumberLabel.backgroundColor = .yellow
        }

    }

}

extension UIStackView {

    func removeAllSubviews() {
        arrangedSubviews.forEach { B1a0a1b.removeFromSuperview() }
    }

    convenience init(frame: CGRect = .zero,
                     alignment: UIStackView.Alignment = .fill,
                     axis: NSLayoutConstraint.Axis = .vertical,
                     distribution: UIStackView.Distribution = .equalSpacing,
                     spacing: CGFloat = 0.0) {

        self.init(frame: frame)

        self.alignment = alignment
        self.axis = axis
        self.distribution = distribution
        self.spacing = spacing
    }
}

和一个示例控制器--我们将创建两个示例,在第二个示例中显示“调试”颜色:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = UIColor(white: 0.95, alpha: 1.0)

        let testView = UserCell(debug: false)
        testView.backgroundColor = .white
        view.addSubview(testView)

        let testViewD = UserCell(debug: true)
        testViewD.backgroundColor = .white
        view.addSubview(testViewD)

        let g = view.safeAreaLayoutGuide

        testView.snp.makeConstraints { (make) in
            make.top.leading.trailing.equalTo(g).inset(16.0)
        }

        testViewD.snp.makeConstraints { (make) in
            make.top.equalTo(testView.snp.bottom).offset(20.0)
            make.leading.trailing.equalTo(testView)
        }

        [testView, testViewD].forEach { v in
            v.layer.cornerRadius = 8.0
            v.layer.shadowColor = UIColor.black.cgColor
            v.layer.shadowRadius = 3.0
            v.layer.shadowOffset = CGSize(width: 0.0, height: 3.0)
            v.layer.shadowOpacity = 0.2
        }

    }

}

下面是输出结果:

相关问题