您可以使用UIBezierPath(黑色边框显示实际视图框架)创建该形状 - 并将其用作蒙版:
基本上,
- 找到从
pt1 到pt2 的直线的中点。
- 找到垂直于该线的点,该点与该线的距离使曲线看起来像您想要的那样。此处显示使用与半行长度相同的长度。
- 创建一个
UIBezierPath,用二次曲线将pt1 连接到pt2。
这是您可以直接在 Playground 页面中运行的示例代码。我根据您发布的图像基于pt1 和pt2 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:
还有 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