如何在 uilabel 上实现左对齐(与本机键盘中出现的建议相同)?

How to achieve left alignment on uilabel (same as suggestions appear in native keyboard)?

下面的屏幕截图描述了我想要实现的目标(我不是 100% 确定如何去做或如何正确解释)。

我已经在我的标签中添加了 allowsDefaultTighteningForTruncation = truelineBreakMode = .byClipping,但它现在显示单词的开头,我需要显示单词的结尾,关于如何实现的任何想法?或任何想法在苹果文档中寻找什么?到目前为止,我已经阅读了我能想到的所有内容。

要获得该结果,您需要将标签嵌入 UIView 并限制标签的尾随而不是前导。

确保“holder”视图的 Clips To Bounds 设置为 true。

随着标签宽度的增加,它将延伸超过支架视图的前缘。

这是一个简单的例子:

class ViewController: UIViewController {

    let theLabel = UILabel()
    let holderView = UIView()
    
    let strs: [String] = [
        "Misinterpret",
        "Misinterpreted",
        "Misinterpretation",
    ]
    var idx = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        theLabel.translatesAutoresizingMaskIntoConstraints = false
        holderView.translatesAutoresizingMaskIntoConstraints = false
        
        holderView.backgroundColor = .systemBlue
        theLabel.backgroundColor = .yellow
        
        theLabel.font = .systemFont(ofSize: 30.0)
        
        holderView.addSubview(theLabel)
        view.addSubview(holderView)
        
        NSLayoutConstraint.activate([
            
            holderView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            holderView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            holderView.widthAnchor.constraint(equalToConstant: 200.0),
            holderView.heightAnchor.constraint(equalTo: theLabel.heightAnchor, constant: 8.0),
            
            theLabel.centerYAnchor.constraint(equalTo: holderView.centerYAnchor),
            theLabel.trailingAnchor.constraint(equalTo: holderView.trailingAnchor, constant: -8.0),
            
        ])
        
        // clip theLabel when it gets too wide
        holderView.clipsToBounds = true
        
        theLabel.text = strs[idx]
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        idx += 1
        theLabel.text = strs[idx % strs.count]
    }
    
}

输出:

“提前输入”建议栏可能也使用了渐变遮罩,因此文本看起来不会被剪裁得如此突然……但这是另一个问题。


编辑 - 这是一个更完整的示例。

  • 顶部的文本字段
  • 灰色“持有人”视图中的标签
  • 显示文本实际大小的绿色标签

当您输入文本时,标签会更新。

灰色框中的标签将水平居中,直到它太宽而无法容纳,此时它将保持“右对齐”。它还将在左边缘有一个轻微的渐变蒙版,因此不会突然被切断。

class ViewController: UIViewController {
    
    let textField = UITextField()
    
    let theClippedLabel = UILabel()
    let holderView = UIView()

    // plain label showing the actual size
    let theActualLabel = UILabel()

    let leftEdgeFadeMask = CAGradientLayer()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        [textField, theClippedLabel, holderView, theActualLabel].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
        }
        
        holderView.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
        theClippedLabel.backgroundColor = .clear
        theActualLabel.backgroundColor = .green
        
        textField.borderStyle = .roundedRect
        textField.placeholder = "Type here..."
        textField.addTarget(self, action: #selector(didEdit(_:)), for: .editingChanged)
        
        theClippedLabel.font = .systemFont(ofSize: 30.0)
        theActualLabel.font = theClippedLabel.font
        
        holderView.addSubview(theClippedLabel)
        view.addSubview(holderView)
        view.addSubview(theActualLabel)
        view.addSubview(textField)
        
        // center label horizontally, unless it is wider than holderView (minus Left/Right "padding")
        let cx = theClippedLabel.centerXAnchor.constraint(equalTo: holderView.centerXAnchor)
        cx.priority = .defaultHigh
        
        NSLayoutConstraint.activate([
            
            // center a 200-pt wide "holder" view
            holderView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            holderView.topAnchor.constraint(equalTo: textField.bottomAnchor, constant: 20.0),
            holderView.widthAnchor.constraint(equalToConstant: 200.0),
            
            // holderView height is 16-pts taller than the label height (8-pts Top / Bottom "padding")
            holderView.heightAnchor.constraint(equalTo: theClippedLabel.heightAnchor, constant: 16.0),
            
            // center the label vertically
            theClippedLabel.centerYAnchor.constraint(equalTo: holderView.centerYAnchor),
            
            // keep the label's Trailing edge at least 8-pts from the holderView's Trailing edge
            theClippedLabel.trailingAnchor.constraint(lessThanOrEqualTo: holderView.trailingAnchor, constant: -8.0),
            
            // activate cx constraint
            cx,
            
            theActualLabel.topAnchor.constraint(equalTo: holderView.bottomAnchor, constant: 4.0),
            theActualLabel.centerXAnchor.constraint(equalTo: holderView.centerXAnchor),
            
            textField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 12.0),
            textField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 12.0),
            textField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -12.0),
            
        ])
        
        // clip theLabel when it gets too wide
        holderView.clipsToBounds = true
        
        // gradient mask for left-edge of label
        leftEdgeFadeMask.colors = [UIColor.clear.cgColor, UIColor.black.cgColor]
        leftEdgeFadeMask.startPoint = CGPoint(x: 0.0, y: 0.0)
        leftEdgeFadeMask.endPoint = CGPoint(x: 1.0, y: 0.0)
        leftEdgeFadeMask.locations = [0.0, 0.1]
        theClippedLabel.layer.mask = leftEdgeFadeMask
        
        // so we have something to see when we start
        theClippedLabel.text = " "
        theActualLabel.text = theClippedLabel.text
    }

    @objc func didEdit(_ sender: Any) {

        // if the textField is empty, use a space character so
        //  the labels don't disappear
        var str = " "
        if let s = textField.text, !s.isEmpty {
            str = s
        }
        theClippedLabel.text = str
        theActualLabel.text = str
        updateMask()

    }
    
    func updateMask() -> Void {
        
        // update label frame
        theClippedLabel.sizeToFit()
        
        // we want the gradient mask to start at the leading edge
        //  of the holder view, with
        //  4-pts Left and 8-pts Right "padding"
        var r = holderView.bounds
        
        let targetW = r.width - 12
        r.size.width -= 12
        r.size.height -= 16
        r.origin.x = theClippedLabel.bounds.width - targetW
        
        // disable built-in layer animations
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        
        leftEdgeFadeMask.frame = r
        
        CATransaction.commit()
    }
    
}

示例结果:

请注意,这只是 示例代码。在实际使用中,我们希望将其构建为自定义视图,其中包含所有大小调整和渐变遮罩逻辑。