在填充的白色圆圈(UIButton)内创建一个细的黑色圆圈(未填充)

时间:2018-08-06 06:04:00

标签: ios swift uibutton calayer

我正在尝试在iOS设备上复制默认的相机按钮:

https://www.iphonefaq.org/images/archives/photo-tip-shutter1.jpg

我能够创建一个带有黑色按钮的白色圆形按钮。但是,黑色按钮也被填充,而不仅仅是一个细圆圈。

这就是我所拥有的(大部分已从不同来源复制并放在一起,因此代码效率不高)

对象代表按钮,

func applyRoundCorner(_ object: AnyObject) {
    //object.layer.shadowColor = UIColor.black.cgColor
    //object.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
    object.layer.cornerRadius = (object.frame.size.width)/2
    object.layer.borderColor = UIColor.black.cgColor
    object.layer.borderWidth = 5
    object.layer.masksToBounds = true
    //object.layer.shadowRadius = 1.0
    //object.layer.shadowOpacity = 0.5

    var CircleLayer   = CAShapeLayer()
    let center = CGPoint (x: object.frame.size.width / 2, y: object.frame.size.height / 2)
    let circleRadius = object.frame.size.width / 6
    let circlePath = UIBezierPath(arcCenter: center, radius: circleRadius, startAngle: CGFloat(M_PI), endAngle: CGFloat(M_PI * 2), clockwise: true)
    CircleLayer.path = circlePath.cgPath
    CircleLayer.strokeColor = UIColor.black.cgColor
    //CircleLayer.fillColor = UIColor.blue.cgColor
    CircleLayer.lineWidth = 1
    CircleLayer.strokeStart = 0
    CircleLayer.strokeEnd  = 1
    object.layer.addSublayer(CircleLayer)
}

3 个答案:

答案 0 :(得分:1)

您只需要添加宽度和高度较小的圆形Shape层

尝试此代码

func applyRoundCorner(_ object: UIButton) {
    object.layer.cornerRadius = (object.frame.size.width)/2
    object.layer.borderColor = UIColor.black.cgColor
    object.layer.borderWidth = 5
    object.layer.masksToBounds = true

    let anotherFrame = CGRect(x: 12, y: 12, width: object.bounds.width - 24, height: object.bounds.height - 24)
    let circle = CAShapeLayer()
    let path = UIBezierPath(arcCenter: object.center, radius: anotherFrame.width / 2, startAngle: 0, endAngle: .pi * 2, clockwise: true)
    circle.path = path.cgPath
    circle.strokeColor = UIColor.black.cgColor
    circle.lineWidth = 1.0
    circle.fillColor = UIColor.clear.cgColor
    object.layer.addSublayer(circle)
}

注意:根据您的要求和最佳用户体验更改框架值

输出

enter image description here

答案 1 :(得分:1)

基本方法

您可以这样做(出于演示的目的,我将使用操场以编程方式进行按钮操作):

let buttonWidth = 100.0

let button = UIButton(frame: CGRect(x: 0, y: 0, width: buttonWidth, height: buttonWidth))
button.backgroundColor = .white
button.layer.cornerRadius = button.frame.width / 2

图纸零件

因此,在添加按钮并进行所需的设置(使其变为圆形)之后,这是在其中绘制圆的部分方法:

let circlePath = UIBezierPath(arcCenter: CGPoint(x: buttonWidth / 2,y: buttonWidth / 2), radius: 40.0, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)

let circleLayer = CAShapeLayer()
circleLayer.path = circlePath.cgPath
circleLayer.fillColor = UIColor.clear.cgColor
circleLayer.strokeColor = UIColor.black.cgColor
circleLayer.lineWidth = 2.5

// adding the layer into the button:
button.layer.addSublayer(circleLayer)

可能circleLayer.fillColor = UIColor.clear.cgColor是您缺少的部分。

因此:

enter image description here


回到您的案例:

旁边的栏提示:

对于实现applyRoundCorner,我建议让它只具有 个圆角视图的工作,然后创建另一个函数以在视图内部添加圆。这是为了避免任何命名冲突,这意味着在阅读“ applyRoundCorner”时,我会认为它也会在我的视图中添加圆圈!所以:

func applyRoundedCorners(for view: UIView) {
    view.layer.cornerRadius = view.frame.size.width / 2
    view.layer.borderColor = UIColor.white.cgColor
    view.layer.borderWidth = 5.0
    view.layer.masksToBounds = true
}

func drawCircle(in view: UIView) {
    let circlePath = UIBezierPath(arcCenter: CGPoint(x: view.frame.size.width / 2,y: view.frame.size.width / 2),
                                             radius: view.frame.size.width / 2.5,
                                             startAngle: 0,
                                             endAngle: CGFloat.pi * 2,
                                             clockwise: true)

    let shapeLayer = CAShapeLayer()
    shapeLayer.path = circlePath.cgPath
    shapeLayer.fillColor = UIColor.clear.cgColor
    shapeLayer.strokeColor = UIColor.black.cgColor
    shapeLayer.lineWidth = 2.5

    button.layer.addSublayer(shapeLayer)
}

现在:

applyRoundedCorners(for: button)
drawCircle(in: button)

这似乎更好。从另一方面来看,您想使视图成为圆形而没有在其中添加圆,使用分离的方法,您可以简单地applyRoundedCorners(for: myView),而无需在其中添加圆。


此外:

如您所见,我将AnyObject更改为UIView,这对于您的情况似乎更合乎逻辑。所以这是我们可以做的一件很酷的事情:

extension UIView {
    func applyRoundedCorners(for view: UIView) {
        view.layer.cornerRadius = view.frame.size.width / 2
        view.layer.borderColor = UIColor.white.cgColor
        view.layer.borderWidth = 5.0
        view.layer.masksToBounds = true
    }

    func drawCircle(in view: UIView) {
        let circlePath = UIBezierPath(arcCenter: CGPoint(x: view.frame.size.width / 2,y: view.frame.size.width / 2),
                                      radius: view.frame.size.width / 2.5,
                                      startAngle: 0,
                                      endAngle: CGFloat.pi * 2,
                                      clockwise: true)

        let shapeLayer = CAShapeLayer()
        shapeLayer.path = circlePath.cgPath
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.lineWidth = 2.5

        button.layer.addSublayer(shapeLayer)
    }
}

现在applyRoundedCornersdrawCircle都隐式包含在UIView中(这意味着UIButton),您无需将button传递给这些函数,而是将能够:

button.applyRoundedCorners()
button.drawCircle()

答案 2 :(得分:1)

毫无疑问,有100万种方法可以解决此问题,这只是一个...

为了简单和快速起见,我从UIButton开始,我可能考虑实际上从UIImage开始并简单地设置按钮的图像属性,但这在很大程度上取决于我试图实现

internal extension FloatingPoint {
    var degreesToRadians: Self { return self * .pi / 180 }
    var radiansToDegrees: Self { return self * 180 / .pi }
}

class RoundButton: UIButton {
    override func draw(_ rect: CGRect) {
        makeButtonImage()?.draw(at: CGPoint(x: 0, y: 0))
    }

    func makeButtonImage() -> UIImage? {
        let size = bounds.size

        UIGraphicsBeginImageContext(CGSize(width: size.width, height: size.height))
        defer {
            UIGraphicsEndImageContext()
        }
        guard let ctx = UIGraphicsGetCurrentContext() else {
            return nil
        }

        let center = CGPoint(x: size.width / 2.0, y: size.height / 2.0)
        // Want to "over fill" the image area, so the mask can be applied
        // to the entire image
        let radius = min(size.width / 2.0, size.height / 2.0)

        let innerRadius = radius * 0.75
        let innerCircle = UIBezierPath(arcCenter: center,
                                                                     radius: innerRadius,
                                                                     startAngle: CGFloat(0.0).degreesToRadians,
                                                                     endAngle: CGFloat(360.0).degreesToRadians,
                                                                     clockwise: true)
        // The color doesn't matter, only it's alpha level
        UIColor.red.setStroke()
        innerCircle.lineWidth = 4.0
        innerCircle.stroke(with: .normal, alpha: 1.0)

        let circle = UIBezierPath(arcCenter: center,
                                                            radius: radius,
                                                            startAngle: CGFloat(0.0).degreesToRadians,
                                                            endAngle: CGFloat(360.0).degreesToRadians,
                                                            clockwise: true)
        UIColor.clear.setFill()
        ctx.fill(bounds)

        UIColor.white.setFill()
        circle.fill(with: .sourceOut, alpha: 1.0)

        return UIGraphicsGetImageFromCurrentImageContext()
    }
}

nb:这是未优化!我会考虑缓存makeButtonImage的结果,并在按钮的状态/大小更改时使结果无效,请注意

为什么这种方法比其他方法“更好”?我只想说,不是,但是它创造的是内圆的“切出”

Playground Result

这是我的选择,但我认为它看起来更好,并且是一种更灵活的解决方案,因为您不需要“内部”笔触颜色,等等,等等,

该解决方案利用了CoreGraphics CGBlendModes

我当然可以在PaintCodeApp中完成整个工作并完成