UIButton中的UIBezierPath圆角

Posted

技术标签:

【中文标题】UIButton中的UIBezierPath圆角【英文标题】:UIBezierPath rounded corners in UIButton 【发布时间】:2017-07-04 11:42:30 【问题描述】:

我想在文本周围绘制一个可点击的气泡形状。为了做到这一点,我决定像这样向 UIButton 添加一个 shapelayer:

// Button also has this
bubbleButton.contentEdgeInsets = UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10)
bubbleButton.setTitle("Gutp", for: .normal)

// In my subclass of UIButton
override func layoutSubviews() 
    super.layoutSubviews()

    self.bubbleLayer?.removeFromSuperlayer()

    let maskLayer = CAShapeLayer.init()
    let bezierPath = UIBezierPath.init(roundedRect: self.bounds,
                                       byRoundingCorners: [.topRight, .topLeft, .bottomLeft],
                                       cornerRadii: CGSize(width: 20, height: 20))
    maskLayer.path = bezierPath.cgPath
    maskLayer.strokeColor = UIColor.red.cgColor
    maskLayer.lineWidth = 2
    maskLayer.fillColor = UIColor.clear.cgColor
    maskLayer.backgroundColor = UIColor.clear.cgColor
    maskLayer.isOpaque = false

    self.bubbleLayer = maskLayer

    if let layers = self.layer.sublayers 
        self.layer.insertSublayer(self.bubbleLayer!, at: UInt32(layers.count))
     else 
        self.layer.addSublayer(self.bubbleLayer!)
    

请不要看现在的表现。

我在 UIStackView 中添加了 2 个这样的按钮。

在某些文本情况下(通常是短的),我得到一个有趣的人工制品(如果可以说是尾巴),在文本较长的情况下得到一个正常的气泡:

我该如何解决这个问题?为什么我会出现这种行为?

编辑:在 bezierPathWithRoundedRect:byRoundingCorners:cornerRadii: 中链接有关损坏的cornerRadii 参数的其他可能相关问题。也许它会帮助有类似问题的人。

    Why is cornerRadii parameter of CGSize type in -[UIBezierPath bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:]?

    Crazy rounded rect UIBezierPath behavior on ios 7. What is the deal?

【问题讨论】:

会不会是你的按钮高度太低了?由于拐角半径为 20,因此需要至少为 40。但通常我不喜欢这些高级路径方法,我会使用直线和圆弧手动创建它。 @MaticOblak 是的,谢谢您的提示。 UIBezierPath.init(roundedRect 似乎坏了。如果我检查我的半径不大于最小边的一半大小,它会按预期工作。如果你愿意,你可以写一个答案,这样我就可以接受了。 【参考方案1】:

要使用这些圆角矩形方法,您必须确保视图大小大于使用的半径。在您的情况下,宽度和高度都必须大于 40(半径大小为 20x20,因此 max(20*2, 20*2) = 40)。

一般来说,我更喜欢使用自定义方法来生成此类路径。使用直线和弧线通常会给您更好的灵活性。您可以尝试以下方法:

    /// Returns a path with rounded corners
    ///
    /// - Parameters:
    ///   - frame: A frame at which the path is drawn. To fit in view "bounds" should be used
    ///   - maximumRadius: A maximum corner radius used. For smaller views radius will be min(width/2, height/2)
    /// - Returns: Returns a new path
    func roundedRectPath(inRect frame: CGRect, radiusConstrainedTo maximumRadius: CGFloat) -> UIBezierPath 

        let radisu = min(maximumRadius, min(frame.size.width*0.5, frame.size.height*0.5))

        let path = UIBezierPath()

        path.move(to: CGPoint(x: frame.origin.x + radisu, y: frame.origin.y)) // Top left
        path.addLine(to: CGPoint(x: frame.origin.x + frame.size.width - radisu, y: frame.origin.y))  // Top right
        path.addQuadCurve(to: CGPoint(x: frame.origin.x + frame.size.width, y: frame.origin.y + frame.size.height - radisu), controlPoint: CGPoint(x: frame.origin.x + frame.size.width, y: frame.origin.y)) // Top right arc
        path.addLine(to: CGPoint(x: frame.origin.x + frame.size.width, y: frame.origin.y + frame.size.height - radisu))  // Bottom right
        path.addQuadCurve(to: CGPoint(x: frame.origin.x + frame.size.width - radisu, y: frame.origin.y + frame.size.height), controlPoint: CGPoint(x: frame.origin.x + frame.size.width, y: frame.origin.y + frame.size.height)) // Bottom right arc
        path.addLine(to: CGPoint(x: frame.origin.x + radisu, y: frame.origin.y + frame.size.height))  // Bottom left
        path.addQuadCurve(to: CGPoint(x: frame.origin.x, y: frame.origin.y + frame.size.height - radisu), controlPoint: CGPoint(x: frame.origin.x, y: frame.origin.y + frame.size.height)) // Bottom left arc
        path.addLine(to: CGPoint(x: frame.origin.x, y: frame.origin.y + radisu))  // Top left
        path.addQuadCurve(to: CGPoint(x: frame.origin.x + radisu, y: frame.origin.y), controlPoint: CGPoint(x: frame.origin.x, y: frame.origin.y)) // Top left arc
        path.close()

        return path
    

在将它与笔画一起使用时,您还需要将框架插入线宽的一半。这是来自“draw rect”过程的 sn-p,但可以在任何地方应用:

        UIColor.red.setStroke()
        let lineWidth: CGFloat = 5.0
        let path = roundedRectPath(inRect: bounds.insetBy(dx: lineWidth*0.5, dy: lineWidth*0.5), radiusConstrainedTo: 30.0)
        path.lineWidth = lineWidth
        path.stroke()

注意bounds.insetBy(dx: lineWidth*0.5, dy: lineWidth*0.5)

【讨论】:

我发现了几个与损坏的 roundedRect 相关的问题:***.com/questions/18880919/… 和 ***.com/questions/24936003/… @iur 很有趣。所以这实际上从来没有奏效。无论如何,我编辑了答案以在抚摸控件时添加线宽的插图。 谢谢。我不太在乎按钮框架外的线条,但它对某人来说绝对有用。 @iur 路径可能会被剪裁,具体取决于设置,这会使线条在侧面看起来比画角的线条更细。 @iur 换句话说:我建议您使用它,这样如果您的视图被剪裁或您的布局发生变化,它就不会在将来破坏您的代码,以便它与笔画交互。超出范围。

以上是关于UIButton中的UIBezierPath圆角的主要内容,如果未能解决你的问题,请参考以下文章

设置UIButton一侧圆角

使用 UIBezierPath 的圆角半径

使用 UIBezierPath 的圆角多边形

如何将圆角添加到 UIBezierPath 自定义矩形?

UITableViewCell自动布局宽度不适用于UIBezierPath

使用 UIBezierPath 的带有圆角的 TableView 页眉和页脚