Swift:在 View 中水平和垂直居中 NSAttributedString

标签 swift constraints

你好,

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

enter image description here

但是,NSAttributedString 只能在容器内的水平维度上居中对齐。

我的解决方案:

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

所以结果看起来像这样:

enter image description here

我的尝试是这样的:

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,将背景设为绿色,并为其设置固定的宽度和高度,以确保它被正确添加为 subview 。它做了。但是一旦我尝试对它进行主动约束,它就会完全消失。

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

最佳答案

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

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

UIGraphicsImageRenderer 方法允许从各种组件(包括 NSAttributedText 和大量自定义形状)制作图像。

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

  1. 渲染具有适当组件和大小的图像
  2. 将呈现的图像分配给按钮

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

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

Rounded rectangles and Emoji with NSAttributedString

下面的代码产生上面的图像:

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)

   }

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

关于Swift:在 View 中水平和垂直居中 NSAttributedString,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54438356/

相关文章:

symfony - 使用映射的 false 和多个 true 验证 symfony 4 formfield 文件类型

ios - 由于未捕获的异常 'NSGenericException' 而终止应用程序,原因 : 'Unable to activate constraint with anchors

ios - 将流媒体视频(m3u8 文件)保存到图库中

ios - 如何从编程的 uibutton 传递数据信息到函数?

ios - 尝试编码按钮按下以将字符串添加到数组时出错

MySQL 约束 - 允许与主表标识符匹配的列值

sql - 重命名 SQL Server 中的约束?

mysql - 了解 MYSQL 中的外键约束

swift - 如何在循环中处理异步函数?

xcode - 为 TableView 创建 IBOutlet 不起作用