多个 UIBezierPath 的渐变
gradient in for multipel UIBezierPath
我应该创建 this。
我确实搜索了 google、youtube 和 Whosebug,下面的代码是我研究的结果。
@IBDesignable class TriangleView2: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
let gradient = CAGradientLayer()
override func draw(_ rect: CGRect) {
//draw the line of UIBezierPath
let path1 = UIBezierPath()
path1.move(to: CGPoint(x: rect.minX - 100, y: rect.maxY - 80))
path1.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
path1.addLine(to: CGPoint(x: (rect.maxX + 90 ), y: rect.minY/2 ))
path1.close()
// add clipping path. this draws an imaginary line (to create bounds) from the
//ends of the UIBezierPath line down to the bottom of the screen
let clippingPath = path1.copy() as! UIBezierPath
clippingPath.move(to: CGPoint(x: rect.minX - 100, y: rect.maxY - 80))
clippingPath.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
clippingPath.addLine(to: CGPoint(x: (rect.maxX + 90 ), y: rect.minY/2 ))
clippingPath.close()
clippingPath.addClip()
// create and add the gradient
let colors = [theme.current.profile_start_view1.cgColor, theme.current.profile_end_view1.cgColor]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let colorLocations:[CGFloat] = [0.0, 1.0]
let gradient = CGGradient(colorsSpace: colorSpace,
colors: colors as CFArray,
locations: colorLocations)
let context = UIGraphicsGetCurrentContext()
let startPoint = CGPoint(x: 1, y: 1)
let endPoint = CGPoint(x: 1, y: bounds.maxY)
// and lastly, draw the gradient.
context!.drawLinearGradient(gradient!, start: startPoint, end:
endPoint, options: CGGradientDrawingOptions.drawsAfterEndLocation)
}
}
对不对,我有 2 个视图(如果我能完成的话,将是 3 个),但有一些不同。结果是 this
这 2 个视图的颜色不同,但正如您所见,两个视图的渐变方向相同
有人有什么建议吗?
您编写的代码始终使用相同的起点和终点颜色,始终使用相同的颜色位置,并且始终使用相同的起点和终点。当然,梯度具有相同方向的相同梯度。
为您的视图提供渐变起点和终点属性以及起点和终点颜色。根据视图的边界,在视图控制器的 layoutDidChange()
方法中设置渐变视图的渐变起点。 (这样你就可以正确处理设备旋转和不同尺寸的设备。
这是一个示例,您可以直接在 playground 中运行。
因为你想要四个渐变并且渐变是使用剪裁绘制的,所以图形上下文被保存并恢复了几次(以重置剪裁)。
放码起止点为剪角之一。那是不需要的。您可以(并且可能)应该使用单独的点。为获得所需的结果,有时您希望在裁剪区域之外使用起点或终点。
import UIKit
import PlaygroundSupport
class TriangleView2: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
let colors = [UIColor(red: 50/255.0, green: 242/255.0, blue: 111/255.0, alpha: 1).cgColor,
UIColor(red: 29/255.0, green: 127/255.0, blue: 60/255.0, alpha: 1).cgColor]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let colorLocations:[CGFloat] = [0.0, 1.0]
let gradient = CGGradient(colorsSpace: colorSpace,
colors: colors as CFArray,
locations: colorLocations)!
let options: CGGradientDrawingOptions = [CGGradientDrawingOptions.drawsBeforeStartLocation, CGGradientDrawingOptions.drawsAfterEndLocation]
let p1 = CGPoint(x: 0, y: 0)
let p2 = CGPoint(x: bounds.width, y: 0)
let p3 = CGPoint(x: bounds.width, y: 20)
let p4 = CGPoint(x: bounds.width / 3, y: 140)
let p5 = CGPoint(x: 0, y: 200)
let p6 = CGPoint(x: bounds.width * 5 / 8, y: 260)
let p7 = CGPoint(x: 0, y: 230)
let p8 = CGPoint(x: bounds.width, y: 280)
let context = UIGraphicsGetCurrentContext()!
context.saveGState()
let path1 = UIBezierPath()
path1.move(to: p1)
path1.addLine(to: p2)
path1.addLine(to: p3)
path1.addLine(to: p4)
path1.close()
path1.addClip()
context.drawLinearGradient(gradient, start: p3, end: p1, options: options)
context.restoreGState()
context.saveGState()
let path2 = UIBezierPath()
path2.move(to: p1)
path2.addLine(to: p4)
path2.addLine(to: p5)
path2.close()
path2.addClip()
context.drawLinearGradient(gradient, start: p1, end: p5, options: options)
context.restoreGState()
context.saveGState()
let path3 = UIBezierPath()
path3.move(to: p3)
path3.addLine(to: p8)
path3.addLine(to: p6)
path3.addLine(to: p4)
path3.close()
path3.addClip()
context.drawLinearGradient(gradient, start: p8, end: p3, options: options)
context.restoreGState()
context.saveGState()
let path4 = UIBezierPath()
path4.move(to: p5)
path4.addLine(to: p4)
path4.addLine(to: p6)
path4.addLine(to: p7)
path4.close()
path4.addClip()
context.drawLinearGradient(gradient, start: p7, end: p6, options: options)
context.restoreGState()
}
}
let main = TriangleView2(frame: CGRect(x: 0, y: 0, width: 320, height: 500))
PlaygroundPage.current.liveView = main
更新
还有一件事:不要使用 rect 参数来导出形状的几何形状。 rect 不指视图大小或位置。相反,它是需要重新绘制的区域。如果 iOS 决定只有部分视图需要重绘,您的代码将绘制错误的形状。
这与Codo的回答有点相似,但你只需要4分。
class FourGradientsView: UIView {
override func draw(_ rect: CGRect) {
let ctx = UIGraphicsGetCurrentContext()!
// Points of area to draw - adjust these 4 variables as needed
let tl = CGPoint(x: 0, y: 0)
let tr = CGPoint(x: bounds.width * 1.3, y: 0)
let bl = CGPoint(x: -bounds.width * 1.8, y: bounds.height * 1.4)
let br = CGPoint(x: bounds.width * 1.3, y: bounds.height * 2)
// Find the intersection of the two crossing diagonals
let s1x = br.x - tl.x
let s1y = br.y - tl.y
let s2x = tr.x - bl.x
let s2y = tr.y - bl.y
//let s = (-s1y * (tl.x - bl.x) + s1x * (tl.y - bl.y)) / (-s2x * s1y + s1x * s2y)
let t = ( s2x * (tl.y - bl.y) - s2y * (tl.x - bl.x)) / (-s2x * s1y + s1x * s2y)
let center = CGPoint(x: tl.x + (t * s1x), y: tl.y + (t * s1y))
// Create clipping region to avoid drawing where we don't want any gradients
ctx.saveGState()
let clip = CGPoint(x: 0, y: bounds.height * 0.7)
let clipPath = UIBezierPath()
clipPath.move(to: CGPoint(x: 0, y: 0))
clipPath.addLine(to: clip)
clipPath.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
clipPath.addLine(to: CGPoint(x: bounds.width, y: 0))
clipPath.close()
clipPath.addClip()
// Use these two colors for all 4 gradients (adjust as needed)
let colors = [
UIColor(hue: 120/360, saturation: 1, brightness: 0.85, alpha: 1).cgColor,
UIColor(hue: 120/360, saturation: 1, brightness: 0.3, alpha: 1).cgColor
] as CFArray
// The common gradient
let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colors, locations: nil)!
// Top gradient
ctx.saveGState()
let pathTop = UIBezierPath()
pathTop.move(to: tl)
pathTop.addLine(to: tr)
pathTop.addLine(to: center)
pathTop.close()
pathTop.addClip()
ctx.drawLinearGradient(gradient, start: CGPoint(x: bounds.width, y: 0), end: CGPoint(x: 0, y: 0), options: [])
ctx.restoreGState()
// Right gradient
ctx.saveGState()
let pathRight = UIBezierPath()
pathRight.move(to: tr)
pathRight.addLine(to: br)
pathRight.addLine(to: center)
pathRight.close()
pathRight.addClip()
ctx.drawLinearGradient(gradient, start: CGPoint(x: bounds.width, y: bounds.height), end: CGPoint(x: bounds.width, y: 0), options: [])
ctx.restoreGState()
// Bottom gradient
ctx.saveGState()
let pathBottom = UIBezierPath()
pathBottom.move(to: br)
pathBottom.addLine(to: bl)
pathBottom.addLine(to: center)
pathBottom.close()
pathBottom.addClip()
ctx.drawLinearGradient(gradient, start: CGPoint(x: 0, y: bounds.height), end: CGPoint(x: bounds.width, y: bounds.height), options: [])
ctx.restoreGState()
// Left gradient
ctx.saveGState()
let pathLeft = UIBezierPath()
pathLeft.move(to: tl)
pathLeft.addLine(to: bl)
pathLeft.addLine(to: center)
pathLeft.close()
pathLeft.addClip()
ctx.drawLinearGradient(gradient, start: CGPoint(x: 0, y: 0), end: CGPoint(x: 0, y: bounds.height), options: [])
ctx.restoreGState()
ctx.restoreGState()
}
}
let grView = FourGradientsView(frame: CGRect(x: 0, y: 0, width: 320, height: 320))
grView.backgroundColor = .black
我应该创建 this。 我确实搜索了 google、youtube 和 Whosebug,下面的代码是我研究的结果。
@IBDesignable class TriangleView2: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
let gradient = CAGradientLayer()
override func draw(_ rect: CGRect) {
//draw the line of UIBezierPath
let path1 = UIBezierPath()
path1.move(to: CGPoint(x: rect.minX - 100, y: rect.maxY - 80))
path1.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
path1.addLine(to: CGPoint(x: (rect.maxX + 90 ), y: rect.minY/2 ))
path1.close()
// add clipping path. this draws an imaginary line (to create bounds) from the
//ends of the UIBezierPath line down to the bottom of the screen
let clippingPath = path1.copy() as! UIBezierPath
clippingPath.move(to: CGPoint(x: rect.minX - 100, y: rect.maxY - 80))
clippingPath.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
clippingPath.addLine(to: CGPoint(x: (rect.maxX + 90 ), y: rect.minY/2 ))
clippingPath.close()
clippingPath.addClip()
// create and add the gradient
let colors = [theme.current.profile_start_view1.cgColor, theme.current.profile_end_view1.cgColor]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let colorLocations:[CGFloat] = [0.0, 1.0]
let gradient = CGGradient(colorsSpace: colorSpace,
colors: colors as CFArray,
locations: colorLocations)
let context = UIGraphicsGetCurrentContext()
let startPoint = CGPoint(x: 1, y: 1)
let endPoint = CGPoint(x: 1, y: bounds.maxY)
// and lastly, draw the gradient.
context!.drawLinearGradient(gradient!, start: startPoint, end:
endPoint, options: CGGradientDrawingOptions.drawsAfterEndLocation)
}
}
对不对,我有 2 个视图(如果我能完成的话,将是 3 个),但有一些不同。结果是 this
这 2 个视图的颜色不同,但正如您所见,两个视图的渐变方向相同
有人有什么建议吗?
您编写的代码始终使用相同的起点和终点颜色,始终使用相同的颜色位置,并且始终使用相同的起点和终点。当然,梯度具有相同方向的相同梯度。
为您的视图提供渐变起点和终点属性以及起点和终点颜色。根据视图的边界,在视图控制器的 layoutDidChange()
方法中设置渐变视图的渐变起点。 (这样你就可以正确处理设备旋转和不同尺寸的设备。
这是一个示例,您可以直接在 playground 中运行。
因为你想要四个渐变并且渐变是使用剪裁绘制的,所以图形上下文被保存并恢复了几次(以重置剪裁)。
放码起止点为剪角之一。那是不需要的。您可以(并且可能)应该使用单独的点。为获得所需的结果,有时您希望在裁剪区域之外使用起点或终点。
import UIKit
import PlaygroundSupport
class TriangleView2: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
let colors = [UIColor(red: 50/255.0, green: 242/255.0, blue: 111/255.0, alpha: 1).cgColor,
UIColor(red: 29/255.0, green: 127/255.0, blue: 60/255.0, alpha: 1).cgColor]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let colorLocations:[CGFloat] = [0.0, 1.0]
let gradient = CGGradient(colorsSpace: colorSpace,
colors: colors as CFArray,
locations: colorLocations)!
let options: CGGradientDrawingOptions = [CGGradientDrawingOptions.drawsBeforeStartLocation, CGGradientDrawingOptions.drawsAfterEndLocation]
let p1 = CGPoint(x: 0, y: 0)
let p2 = CGPoint(x: bounds.width, y: 0)
let p3 = CGPoint(x: bounds.width, y: 20)
let p4 = CGPoint(x: bounds.width / 3, y: 140)
let p5 = CGPoint(x: 0, y: 200)
let p6 = CGPoint(x: bounds.width * 5 / 8, y: 260)
let p7 = CGPoint(x: 0, y: 230)
let p8 = CGPoint(x: bounds.width, y: 280)
let context = UIGraphicsGetCurrentContext()!
context.saveGState()
let path1 = UIBezierPath()
path1.move(to: p1)
path1.addLine(to: p2)
path1.addLine(to: p3)
path1.addLine(to: p4)
path1.close()
path1.addClip()
context.drawLinearGradient(gradient, start: p3, end: p1, options: options)
context.restoreGState()
context.saveGState()
let path2 = UIBezierPath()
path2.move(to: p1)
path2.addLine(to: p4)
path2.addLine(to: p5)
path2.close()
path2.addClip()
context.drawLinearGradient(gradient, start: p1, end: p5, options: options)
context.restoreGState()
context.saveGState()
let path3 = UIBezierPath()
path3.move(to: p3)
path3.addLine(to: p8)
path3.addLine(to: p6)
path3.addLine(to: p4)
path3.close()
path3.addClip()
context.drawLinearGradient(gradient, start: p8, end: p3, options: options)
context.restoreGState()
context.saveGState()
let path4 = UIBezierPath()
path4.move(to: p5)
path4.addLine(to: p4)
path4.addLine(to: p6)
path4.addLine(to: p7)
path4.close()
path4.addClip()
context.drawLinearGradient(gradient, start: p7, end: p6, options: options)
context.restoreGState()
}
}
let main = TriangleView2(frame: CGRect(x: 0, y: 0, width: 320, height: 500))
PlaygroundPage.current.liveView = main
更新
还有一件事:不要使用 rect 参数来导出形状的几何形状。 rect 不指视图大小或位置。相反,它是需要重新绘制的区域。如果 iOS 决定只有部分视图需要重绘,您的代码将绘制错误的形状。
这与Codo的回答有点相似,但你只需要4分。
class FourGradientsView: UIView {
override func draw(_ rect: CGRect) {
let ctx = UIGraphicsGetCurrentContext()!
// Points of area to draw - adjust these 4 variables as needed
let tl = CGPoint(x: 0, y: 0)
let tr = CGPoint(x: bounds.width * 1.3, y: 0)
let bl = CGPoint(x: -bounds.width * 1.8, y: bounds.height * 1.4)
let br = CGPoint(x: bounds.width * 1.3, y: bounds.height * 2)
// Find the intersection of the two crossing diagonals
let s1x = br.x - tl.x
let s1y = br.y - tl.y
let s2x = tr.x - bl.x
let s2y = tr.y - bl.y
//let s = (-s1y * (tl.x - bl.x) + s1x * (tl.y - bl.y)) / (-s2x * s1y + s1x * s2y)
let t = ( s2x * (tl.y - bl.y) - s2y * (tl.x - bl.x)) / (-s2x * s1y + s1x * s2y)
let center = CGPoint(x: tl.x + (t * s1x), y: tl.y + (t * s1y))
// Create clipping region to avoid drawing where we don't want any gradients
ctx.saveGState()
let clip = CGPoint(x: 0, y: bounds.height * 0.7)
let clipPath = UIBezierPath()
clipPath.move(to: CGPoint(x: 0, y: 0))
clipPath.addLine(to: clip)
clipPath.addLine(to: CGPoint(x: bounds.width, y: bounds.height))
clipPath.addLine(to: CGPoint(x: bounds.width, y: 0))
clipPath.close()
clipPath.addClip()
// Use these two colors for all 4 gradients (adjust as needed)
let colors = [
UIColor(hue: 120/360, saturation: 1, brightness: 0.85, alpha: 1).cgColor,
UIColor(hue: 120/360, saturation: 1, brightness: 0.3, alpha: 1).cgColor
] as CFArray
// The common gradient
let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colors, locations: nil)!
// Top gradient
ctx.saveGState()
let pathTop = UIBezierPath()
pathTop.move(to: tl)
pathTop.addLine(to: tr)
pathTop.addLine(to: center)
pathTop.close()
pathTop.addClip()
ctx.drawLinearGradient(gradient, start: CGPoint(x: bounds.width, y: 0), end: CGPoint(x: 0, y: 0), options: [])
ctx.restoreGState()
// Right gradient
ctx.saveGState()
let pathRight = UIBezierPath()
pathRight.move(to: tr)
pathRight.addLine(to: br)
pathRight.addLine(to: center)
pathRight.close()
pathRight.addClip()
ctx.drawLinearGradient(gradient, start: CGPoint(x: bounds.width, y: bounds.height), end: CGPoint(x: bounds.width, y: 0), options: [])
ctx.restoreGState()
// Bottom gradient
ctx.saveGState()
let pathBottom = UIBezierPath()
pathBottom.move(to: br)
pathBottom.addLine(to: bl)
pathBottom.addLine(to: center)
pathBottom.close()
pathBottom.addClip()
ctx.drawLinearGradient(gradient, start: CGPoint(x: 0, y: bounds.height), end: CGPoint(x: bounds.width, y: bounds.height), options: [])
ctx.restoreGState()
// Left gradient
ctx.saveGState()
let pathLeft = UIBezierPath()
pathLeft.move(to: tl)
pathLeft.addLine(to: bl)
pathLeft.addLine(to: center)
pathLeft.close()
pathLeft.addClip()
ctx.drawLinearGradient(gradient, start: CGPoint(x: 0, y: 0), end: CGPoint(x: 0, y: bounds.height), options: [])
ctx.restoreGState()
ctx.restoreGState()
}
}
let grView = FourGradientsView(frame: CGRect(x: 0, y: 0, width: 320, height: 320))
grView.backgroundColor = .black