如何将视图边缘弯曲成弧形

时间:2018-10-31 16:10:09

标签: ios swift

我想使视图看起来像这样:

如果有办法,可以用预定义的角度定义点A,点B。

sample

我发现的唯一解决方案是制作一个巨大的圆形视图,并使用clipToBounds = true将其插入为另一个视图的子视图。但这在多个屏幕尺寸上都存在一些问题,因为我正在使用约束。


Edit1:经过一番搜索,我试图使用CAShapeLayer创建该视图,但没有成功。我正在按故事板创建具有约束的视图,该视图也由IBOutlet和您的主要约束连接在一起。这是代码:

在viewDidLoad上:

self.cnstRoundedLeading.constant = -(self.vwRounded.frame.width/3)
let maskPath : UIBezierPath = UIBezierPath(roundedRect: CGRect(x: self.vwRounded.bounds.minX*4,
                                                                   y: self.vwRounded.bounds.minY*4,
                                                                   width: self.vwRounded.bounds.width*4,
                                                                   height: self.vwRounded.bounds.height*4),
                                                                    byRoundingCorners: .topLeft,
                                                                    cornerRadii: CGSize(width: self.vwRounded.frame.size.width*2,
                                                                                        height: self.vwRounded.frame.size.height))



let maskLayer : CAShapeLayer = CAShapeLayer()
    maskLayer.frame = self.vwRounded.bounds
    maskLayer.path = maskPath.cgPath
    self.vwRounded.layer.mask = maskLayer

在viewWillLayoutSubviews上

gradient2.colors = [startColorBlue.cgColor, endColorBlue.cgColor]
gradient2.locations = [0.0, 1.0]
gradient2.startPoint = CGPoint(x: 0, y: 1)
gradient2.endPoint = CGPoint(x: 1, y: 0)
vwRounded.applyGradient(gradient2)

applyGradient它是UIView的扩展:

func applyGradient(_ gradient: CAGradientLayer) -> Void {
        gradient.frame = self.bounds
        self.layer.insertSublayer(gradient, at: 0)
    }

无法正常工作,我不知道构建“弧形边缘”效果的正确方法

1 个答案:

答案 0 :(得分:0)

您可以使用UIBezierPath创建该形状-并将其用作遮罩(黑色边框显示实际的视图框架):

enter image description here

基本上

  • 找到从pt1pt2的直线的中点。
  • 找到垂直于该线的点,并与该线保持一定距离,以使曲线看起来像您想要的方式。此处显示的是使用与行长一半相同的长度。
  • 创建一个UIBezierPath,并用二次曲线将pt1连接到pt2

这是示例代码,您可以直接在Playground页面中运行。我根据发布的图片确定了pt1pt2的y位置...如果更改视图的框架,它将保持显示的比例。

import PlaygroundSupport
import UIKit

class TestViewController: UIViewController {

    override public var preferredContentSize: CGSize {
        get { return CGSize(width: 800, height: 800) }
        set { super.preferredContentSize = newValue }
    }

    let myPlainView: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    let myBorderView: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    func customCurvedPath(for rect: CGRect) -> UIBezierPath {

        // curve start point Y is 490/544ths of the height of the view
        let curveStartPoint = CGPoint(x: 0.0, y: rect.size.height * 490.0 / 544.0)

        // curve end point Y is 22/544ths of the height of the view
        let curveEndPoint = CGPoint(x: rect.size.width, y: rect.size.height * 22.0 / 544.0)

        var x1 = curveStartPoint.x
        var y1 = curveStartPoint.y

        let x2 = curveEndPoint.x
        let y2 = curveEndPoint.y

        // get the midpoint of the line from x1,y1 to x2,y2
        x1 = (x1 + x2) / 2.0
        y1 = (y1 + y2) / 2.0

        // get the length of half the line (midpoint to endpoint)
        var dx = x1 - x2
        var dy = y1 - y2
        let dist = sqrt(dx*dx + dy*dy)

        // use length of helf the line for distance from line
        // increase or decrease this value to get the desired curve
        let distFromLine = dist

        dx /= dist
        dy /= dist

        // get perpendicular point at distFromLine
        let x3 = x1 - (distFromLine/2)*dy
        let y3 = y1 + (distFromLine/2)*dx

        let curveControlPoint = CGPoint(x: x3, y: y3)

        let myBezier = UIBezierPath()

        // pt1
        myBezier.move(to: curveStartPoint)

        // quad curve to pt2
        myBezier.addQuadCurve(to: curveEndPoint, controlPoint: curveControlPoint)

        // line to pt3 (bottom right corner)
        myBezier.addLine(to: CGPoint(x: rect.width, y: rect.height))

        // line to pt4 (bottom left corner)
        myBezier.addLine(to: CGPoint(x: 0.0, y: rect.height))

        // close the path (automatically add a line from bottom left corner to curve start point)
        myBezier.close()

        return myBezier

    }

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white

        let vwWidth = CGFloat(710.0)
        let vwHeight = CGFloat(544.0)

        view.addSubview(myBorderView)

        myBorderView.backgroundColor = .clear

        NSLayoutConstraint.activate([

            myBorderView.widthAnchor.constraint(equalToConstant: vwWidth + 2.0),
            myBorderView.heightAnchor.constraint(equalToConstant: vwHeight + 2.0),
            myBorderView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            myBorderView.centerYAnchor.constraint(equalTo: view.centerYAnchor),

            ])

        myBorderView.layer.borderWidth = 2.0

        // comment this next line (or set to false) to see the actual view frame
        myBorderView.isHidden = true

        view.addSubview(myPlainView)

        myPlainView.backgroundColor = .red

        NSLayoutConstraint.activate([

            myPlainView.widthAnchor.constraint(equalToConstant: vwWidth),
            myPlainView.heightAnchor.constraint(equalToConstant: vwHeight),
            myPlainView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            myPlainView.centerYAnchor.constraint(equalTo: view.centerYAnchor),

            ])

        let bezPath = customCurvedPath(for: CGRect(x: 0, y: 0, width: vwWidth, height: vwHeight))

        // add the bezier path as a layer mask
        let maskForPath = CAShapeLayer()
        maskForPath.path = bezPath.cgPath
        myPlainView.layer.mask = maskForPath

    }

}

let viewController = TestViewController()

PlaygroundPage.current.liveView = viewController

正如我所提到的,这可以作为自定义视图类的一部分更好地工作,因为您可以覆盖layoutSubviews()以使路径形状保持一致。

下面是在自定义视图中使用渐变图层+图层蒙版的示例,设置为300 x 250

enter image description here

以及可在Playground上运行的源代码

import PlaygroundSupport
import UIKit

class MaskedGradientView: UIView {

    var gradLayer: CAGradientLayer!

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    func commonInit() -> Void {
        gradLayer = CAGradientLayer()
        gradLayer.colors = [UIColor.blue.cgColor, UIColor.cyan.cgColor]
        gradLayer.locations = [0.0, 1.0]
        gradLayer.startPoint = CGPoint(x: 0, y: 1)
        gradLayer.endPoint = CGPoint(x: 1, y: 0)
        layer.addSublayer(gradLayer)
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        let rect = self.bounds

        gradLayer.frame = self.bounds

        // curve start point Y is 490/544ths of the height of the view
        let curveStartPoint = CGPoint(x: 0.0, y: rect.size.height * 490.0 / 544.0)

        // curve end point Y is 22/544ths of the height of the view
        let curveEndPoint = CGPoint(x: rect.size.width, y: rect.size.height * 22.0 / 544.0)

        var x1 = curveStartPoint.x
        var y1 = curveStartPoint.y

        let x2 = curveEndPoint.x
        let y2 = curveEndPoint.y

        // get the midpoint of the line from x1,y1 to x2,y2
        x1 = (x1 + x2) / 2.0
        y1 = (y1 + y2) / 2.0

        // get the length of half the line (midpoint to endpoint)
        var dx = x1 - x2
        var dy = y1 - y2
        let dist = sqrt(dx*dx + dy*dy)

        // use length of helf the line for distance from line
        // increase or decrease this value to get the desired curve
        let distFromLine = dist

        dx /= dist
        dy /= dist

        // get perpendicular point at distFromLine
        let x3 = x1 - (distFromLine/2)*dy
        let y3 = y1 + (distFromLine/2)*dx

        let curveControlPoint = CGPoint(x: x3, y: y3)

        let myBezier = UIBezierPath()

        // pt1
        myBezier.move(to: curveStartPoint)

        // quad curve to pt2
        myBezier.addQuadCurve(to: curveEndPoint, controlPoint: curveControlPoint)

        // line to pt3 (bottom right corner)
        myBezier.addLine(to: CGPoint(x: rect.width, y: rect.height))

        // line to pt4 (bottom left corner)
        myBezier.addLine(to: CGPoint(x: 0.0, y: rect.height))

        // close the path (automatically add a line from bottom left corner to curve start point)
        myBezier.close()

        // add the bezier path as a layer mask
        let maskForPath = CAShapeLayer()
        maskForPath.path = myBezier.cgPath
        layer.mask = maskForPath

    }

}

class TestViewController: UIViewController {

    override public var preferredContentSize: CGSize {
        get { return CGSize(width: 400, height: 400) }
        set { super.preferredContentSize = newValue }
    }

    let myMaskedGradientView: MaskedGradientView = {
        let v = MaskedGradientView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    let myPlainView: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .blue
        return v
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white

        view.addSubview(myMaskedGradientView)

        NSLayoutConstraint.activate([

            myMaskedGradientView.widthAnchor.constraint(equalToConstant: 300.0),
            myMaskedGradientView.heightAnchor.constraint(equalToConstant: 250.0),
            myMaskedGradientView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            myMaskedGradientView.centerYAnchor.constraint(equalTo: view.centerYAnchor),

            ])

    }

}

let viewController = TestViewController()

PlaygroundPage.current.liveView = viewController