首页 > 解决方案 > Swift:在视图中水平和垂直居中 NSAttributedString

问题描述

你好呀,

我有一个类 CardButton: UIButton 。在这个 CardButton 的 draw 方法中,我想在其中添加和居中(垂直和水平)NSAttributed String,它基本上只是一个 Emoji。结果看起来像这样:

在此处输入图像描述

但是,NSAttributedString 只能对齐到容器内水平维度的中心。

我对解决方案的想法:

  1. 在 CardButton 内创建一个 containerView
  2. 在它的容器(即 CardButton)中垂直和水平地居中 containerView
  3. 在 containerView 中添加 NSAttributedString 并调整 containerView 的大小以适应字符串的字体。

所以结果看起来像这样:

在此处输入图像描述

我的尝试是这样的:

class CardButton: UIButton {

override func draw(){
//succesfully drawing the CardButton

let stringToDraw = attributedString(symbol, fontSize: symbolFontSize) //custom method to create attributed string

let containerView = UIView()
containerView.backgroundColor = //green
addSubview(containerView)

containerView.translatesAutoresizingMaskIntoConstraints = false
let centerXConstraint = containerView.centerXAnchor.constraint(equalTo: (self.centerXAnchor)).isActive = true
let centerYConstraint = containerView.centerYAnchor.constraint(equalTo: (self.centerYAnchor)).isActive = true

stringToDraw.draw(in: containerView.bounds)
containerView.sizeToFit()

}
}

长话短说,我非常失败。我首先尝试将 containerView 添加到 cardButton,将背景设为绿色,并为其提供固定的宽度和高度,以确保它被正确添加为子视图。它做了。但是一旦我尝试对其进行主动约束,它就完全消失了。

知道如何解决这个问题吗?

标签: swiftconstraints

解决方案


实现任何特定 UI 设计目标的方法总是不止一种,但下面的过程相对简单,并且已根据您的问题中提出和理解的要求进行了调整。

UIButton.setImage 方法允许将图像分配给 UIButton 而无需显式创建容器。

UIGraphicsImageRenderer 方法允许使用各种组件制作图像,包括 NSAttributedText 和许多自定义形状。

使用这两个工具为您的项目提供基础的过程将是:

  1. 使用适当的组件和大小渲染图像
  2. 将渲染图像分配给按钮

可以为这个功能创建一个类,但这里没有探讨。

此外,您的问题提到应用约束时内容会消失。当图像尺寸对于容器来说太大时,可以观察到这种效果,约束将内容定位在视野之外以及可能存在大量其他条件。

圆角矩形和带有 NSAttributedString 的 Emoji

以下代码生成上面的图像:

func drawRectangleWithEmoji() -> UIImage {
    let renderer = UIGraphicsImageRenderer(size: CGSize(width: 512, height: 512))

    let img = renderer.image { (ctx) in
        // Create the outer square:
        var rectangle = CGRect(x: 0, y: 0, width: 512, height: 512).insetBy(dx: 7.5, dy: 7.5)
        var roundedRect = UIBezierPath(roundedRect: rectangle, cornerRadius: 50).cgPath
        // MARK: .cgPath creates a CG representation of the path

        ctx.cgContext.setFillColor(UIColor.white.cgColor)
        ctx.cgContext.setStrokeColor(UIColor.blue.cgColor)
        ctx.cgContext.setLineWidth(15)

        ctx.cgContext.addPath(roundedRect)
        ctx.cgContext.drawPath(using: .fillStroke)

        // Create the inner square:
        rectangle = CGRect(x: 0, y: 0, width: 512, height: 512).insetBy(dx: 180, dy: 180)
        roundedRect = UIBezierPath(roundedRect: rectangle, cornerRadius: 10).cgPath

        ctx.cgContext.setFillColor(UIColor.green.cgColor)
        ctx.cgContext.setStrokeColor(UIColor.green.cgColor)
        ctx.cgContext.setLineWidth(15)

        ctx.cgContext.addPath(roundedRect)
        ctx.cgContext.drawPath(using: .fillStroke)

        // Add emoji:
        var fontSize: CGFloat = 144
        var attrs: [NSAttributedString.Key: Any] = [
            .font: UIFont.systemFont(ofSize: fontSize)//,
            //.backgroundColor: UIColor.gray  //uncomment to see emoji background bounds
        ]

        var string = "❤️"
        var attributedString = NSAttributedString(string: string, attributes: attrs)
        let strWidth = attributedString.size().width
        let strHeight = attributedString.size().height
        attributedString.draw(at: CGPoint(x: 256 - strWidth / 2, y: 256 - strHeight / 2))

        // Add NSAttributedString:
        fontSize = 56
        attrs = [
            .font: UIFont.systemFont(ofSize: fontSize),
            .foregroundColor: UIColor.brown
        ]

        string = "NSAttributedString"
        attributedString = NSAttributedString(string: string, attributes: attrs)
        let textWidth = attributedString.size().width
        let textHeight = attributedString.size().height
        attributedString.draw(at: CGPoint(x: 256 - textWidth / 2, y: 384 - textHeight / 2))

    }

    return img
}

激活 NSLayoutContraints 然后可以为按钮设置新图像:

    override func viewDidLoad() {
       super.viewDidLoad()
       view = UIView()
       view.backgroundColor = .white

       let buttonsView = UIView()
       buttonsView.translatesAutoresizingMaskIntoConstraints = false
       // buttonsView.backgroundColor = UIColor.gray
       view.addSubview(buttonsView)

       NSLayoutConstraint.activate([
           buttonsView.widthAnchor.constraint(equalToConstant: CGFloat(buttonWidth)),
           buttonsView.heightAnchor.constraint(equalToConstant: CGFloat(buttonWidth)),
           buttonsView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
           buttonsView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
           ])

       let cardButton = UIButton(type: .custom)
       cardButton.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
       cardButton.setImage(drawRectangleWithEmoji(), for: .normal)

       let frame = CGRect(x: 0, y: 0, width: buttonWidth, height: buttonWidth)
       cardButton.frame = frame

       buttonsView.addSubview(cardButton)

   }

我们将不胜感激您的意见以及对所提供代码的建设性审查。


推荐阅读