如何在LabelView中添加圆点(省略号),就像在一本书的目录中一样。

问题描述 投票:0回答:1

我有一个stackView,其中包含了几个labelView,每个labelView中写了两个字。我想让它们在整个labelView的宽度上用一个省略号分开。结果是:一个词靠近左边,另一个词靠近右边,它们之间有点。注意:如果单词长度较长,标签可能会占用好几行。

enter image description here

编辑

在这里填满我的堆栈查看

for ingredient in ingredients {
    let textLabel = UILabel()
    textLabel.backgroundColor = UIColor.yellow // just for my needs
    textLabel.widthAnchor.constraint(equalToConstant: ingredientsStackView.frame.width).isActive = true
    textLabel.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
    textLabel.text = ingredient.getName() + " " + String(ingredient.getAmount()) + " " + ingredient.getMeasure()
    textLabel.textAlignment = .left
    textLabel.numberOfLines = 0
    textLabel.lineBreakMode = .byWordWrapping
    ingredientsStackView.addArrangedSubview(textLabel)
}
ingredientsStackView.translatesAutoresizingMaskIntoConstraints = false

而这样子

enter image description here

但我想要这样的东西

enter image description here

你可以看到点与点之间 ingredientNameingredientAmount.

我有一个想法,通过 CGFloat 转换 此处,但这个问题已被关闭。

ios swift uilabel
1个回答
3
投票

一种技术是使用 size()boundingRect(with:options:context:) 来计算大小,重复计算越来越多的点,直到你达到所需的宽度。

但这忽略了一个微妙的(但我认为是重要的)方面,即所有行的点都应该完美地排列起来。如果它们不一致,就会令人惊讶地分心。

所以,我倾向于定义一个能做到这一点的视图,对一些共同的祖先视图坐标系进行模数计算。而且,我个人会将这些点渲染为 UIBezierPath.

例如: 。

class EllipsesView: UIView {
    let spacing: CGFloat = 3
    let radius: CGFloat = 1.5

    var color: UIColor {
        UIColor { traitCollection in
            switch traitCollection.userInterfaceStyle {
            case .dark: return .white
            default:    return .black
            }
        }
    }

    let shapeLayer: CAShapeLayer = {
        let layer = CAShapeLayer()
        layer.strokeColor = UIColor.clear.cgColor
        return layer
    }()

    override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        configure()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        configure()
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        shapeLayer.fillColor = color.cgColor

        let point = convert(bounds.origin, to: window)

        let diff = radius * 3 + spacing
        let offset = diff - point.x.truncatingRemainder(dividingBy: diff)

        let rect = CGRect(x: bounds.minX + offset, y: bounds.maxY - radius * 2, width: bounds.width - offset, height: radius * 2)

        let path = UIBezierPath()

        var center = CGPoint(x: rect.minX + radius, y: rect.midY)

        while center.x + radius < rect.maxX {
            path.addArc(withCenter: center, radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: true)
            center.x += diff
        }

        shapeLayer.path = path.cgPath
    }
}

private extension EllipsesView {
    func configure() {
        layer.addSublayer(shapeLayer)
    }
}

然后你可以添加你的两个标签, 将椭圆视图的底部与标签的底部基线对齐。

let stringPairs = [("foo", "$1.37"), ("foobar", "$0.42"), ("foobarbaz", "$10.00"), ("foobarbazqux", "$100.00")]
for stringPair in stringPairs {
    let container = UIView()
    container.translatesAutoresizingMaskIntoConstraints = false

    let leftLabel = UILabel()
    leftLabel.translatesAutoresizingMaskIntoConstraints = false
    leftLabel.text = stringPair.0
    leftLabel.setContentHuggingPriority(.required, for: .horizontal)
    container.addSubview(leftLabel)

    let ellipsesView = EllipsesView()
    ellipsesView.translatesAutoresizingMaskIntoConstraints = false
    container.addSubview(ellipsesView)

    let rightLabel = UILabel()
    rightLabel.translatesAutoresizingMaskIntoConstraints = false
    rightLabel.font = UIFont.monospacedDigitSystemFont(ofSize: rightLabel.font.pointSize, weight: .regular)
    rightLabel.text = stringPair.1
    rightLabel.setContentHuggingPriority(.required, for: .horizontal)
    container.addSubview(rightLabel)

    NSLayoutConstraint.activate([
        // horizontal constraints

        leftLabel.leadingAnchor.constraint(equalTo: container.leadingAnchor),
        ellipsesView.leadingAnchor.constraint(equalTo: leftLabel.trailingAnchor, constant: 3),
        rightLabel.leadingAnchor.constraint(equalTo: ellipsesView.trailingAnchor, constant: 3),
        rightLabel.trailingAnchor.constraint(equalTo: container.trailingAnchor),

        // align last baseline of three subviews

        leftLabel.lastBaselineAnchor.constraint(equalTo: ellipsesView.bottomAnchor),
        leftLabel.lastBaselineAnchor.constraint(equalTo: rightLabel.lastBaselineAnchor),

        // vertical constraints to container

        leftLabel.topAnchor.constraint(greaterThanOrEqualTo: container.topAnchor),
        rightLabel.topAnchor.constraint(greaterThanOrEqualTo: container.topAnchor),
        ellipsesView.topAnchor.constraint(equalTo: container.topAnchor),
        leftLabel.bottomAnchor.constraint(equalTo: container.bottomAnchor),
    ])

    verticalStackView.addArrangedSubview(container)
}

这样就产生了椭圆,但它们也都完美地排列在一起了。

enter image description here


0
投票

谢谢 @koen. 他给了我另一个问题的链接,也帮助了我。但我看到他删除了那条评论,也许是因为Rob的答案。我也没能保存下来。

我的想法是在根垂直堆栈上添加3个视图的水平堆栈。

let stackView = UIStackView()
ingredientsStackView.addArrangedSubview(stackView)
stackView.leadingAnchor.constraint(equalTo: ingredientsStackView.leadingAnchor,
                                              constant: 0.0).isActive = true
stackView.trailingAnchor.constraint(equalTo: ingredientsStackView.trailingAnchor,
                                               constant: 0.0).isActive = true

接下来为这些视图创建并设置约束条件

let nameLabel = UILabel()
let ellipsesLabel = UILabel()
let amountAndMeasureLabel = UILabel()
for label in [nameLabel, ellipsisLabel, amountAndMeasureLabel] {
    label.font = UIFont(name: "Montserrat-Medium", size: 14)
    stackView.addArrangedSubview(label)
}
NSLayoutConstraint.activate([
    nameLabel.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 0.0),
    nameLabel.trailingAnchor.constraint(equalTo: ellipsisLabel.leadingAnchor, constant: 0.0),
    nameLabel.widthAnchor.constraint(equalToConstant: nameLabel.intrinsicContentSize.width),
    amountAndMeasureLabel.leadingAnchor.constraint(equalTo: ellipsisLabel.trailingAnchor, constant: 0.0),
    amountAndMeasureLabel.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: 0.0),
    amountAndMeasureLabel.widthAnchor.constraint(equalToConstant: amountAndMeasureLabel.intrinsicContentSize.width)
])

之后 不要忘了 呼叫 stackView.layoutIfNeeded()

然后在中间的视图中填充点,直到完全填满。

while ellipsisLabel.intrinsicContentSize.width <= ellipsisLabel.frame.width {
    ellipsisLabel.text?.append(".")
}

整段代码

private func setIngredients(ingredients: [Ingredient], ingredientsStackView: UIStackView) {
        for ingredient in ingredients {
            let stackView = UIStackView()
            stackView.axis = .horizontal
            stackView.alignment = .fill
            stackView.distribution = .fill
            ingredientsStackView.addArrangedSubview(stackView)
            stackView.leadingAnchor.constraint(equalTo: ingredientsStackView.leadingAnchor,
                                               constant: 0.0).isActive = true
            stackView.trailingAnchor.constraint(equalTo: ingredientsStackView.trailingAnchor,
                                                constant: 0.0).isActive = true

            let nameLabel = UILabel()
            nameLabel.text = ingredient.name
            nameLabel.lineBreakMode = .byWordWrapping
            nameLabel.contentMode = .left
            nameLabel.numberOfLines = 0
            let ellipsisLabel = UILabel()
            ellipsisLabel.text = ""
            let amountAndMeasureLabel = UILabel()
            amountAndMeasureLabel.text = String(ingredient.amount) + " " + ingredient.measure
            amountAndMeasureLabel.contentMode = .left
            for label in [nameLabel, ellipsisLabel, amountAndMeasureLabel] {
                label.font = UIFont(name: "Montserrat-Medium", size: 14)
                stackView.addArrangedSubview(label)
            }
            NSLayoutConstraint.activate([
                nameLabel.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 0.0),
                nameLabel.trailingAnchor.constraint(equalTo: ellipsisLabel.leadingAnchor, constant: 0.0),
                nameLabel.widthAnchor.constraint(
                    equalToConstant: nameLabel.intrinsicContentSize.width),
                amountAndMeasureLabel.leadingAnchor.constraint(equalTo: ellipsisLabel.trailingAnchor, constant: 0.0),
                amountAndMeasureLabel.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: 0.0),
                amountAndMeasureLabel.widthAnchor.constraint(
                    equalToConstant: amountAndMeasureLabel.intrinsicContentSize.width)
            ])
            stackView.layoutIfNeeded()

            while ellipsisLabel.intrinsicContentSize.width <= ellipsisLabel.frame.width {
                ellipsisLabel.text?.append(".")
            }
        }
        ingredientsStackView.translatesAutoresizingMaskIntoConstraints = false
    }

但是在看到@Rob的回答之前,我就做了这个。谢谢你 可能和我的相比,实现起来比较困难,你可以选择哪个更适合你。我觉得他的方案更合理,更简洁,所以作为答案我选择他的帖子。

© www.soinside.com 2019 - 2024. All rights reserved.