iOS 以编程方式渐变
iOS Programmatically Gradient
如何以编程方式实现与附加图像中完全相同的渐变?
答案在很大程度上取决于设计师用来创建渐变的内容。有一些工具(例如 Invision)可以为开发人员提供有用的信息,例如渐变颜色和点。但是,如果他们只是在 Photoshop 中创建它,则很难以编程方式重现,也许您应该索取渐变图像。
另一种解决方案是在 Xcode 中使用 @IBDesignable
渐变视图;许多开发人员创建了它们,包括我,主要是因为过去不得不处理很多这个问题。这种方法的一个主要优点是可以非常容易地调整颜色,甚至可以根据需要设置动画。
我有一个开源示例available on GitHub, plus I wrote an extended explanation of how it works。
我快速尝试使用我的项目制作类似您的渐变,这就是它在 Xcode 中的样子:-
关键部分是“属性检查器”面板的顶部,您可以在其中轻松设置开始和结束渐变颜色以及渐变角度 - 您的角度看起来约为 310 度。
下面粘贴了您需要的主要代码。按照上面的链接了解其工作原理。
import UIKit
@IBDesignable
class LDGradientView: UIView {
// the gradient start colour
@IBInspectable var startColor: UIColor? {
didSet {
updateGradient()
}
}
// the gradient end colour
@IBInspectable var endColor: UIColor? {
didSet {
updateGradient()
}
}
// the gradient angle, in degrees anticlockwise from 0 (east/right)
@IBInspectable var angle: CGFloat = 270 {
didSet {
updateGradient()
}
}
// the gradient layer
private var gradient: CAGradientLayer?
// initializers
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
installGradient()
}
override init(frame: CGRect) {
super.init(frame: frame)
installGradient()
}
// Create a gradient and install it on the layer
private func installGradient() {
// if there's already a gradient installed on the layer, remove it
if let gradient = self.gradient {
gradient.removeFromSuperlayer()
}
let gradient = createGradient()
self.layer.addSublayer(gradient)
self.gradient = gradient
}
// Update an existing gradient
private func updateGradient() {
if let gradient = self.gradient {
let startColor = self.startColor ?? UIColor.clear
let endColor = self.endColor ?? UIColor.clear
gradient.colors = [startColor.cgColor, endColor.cgColor]
let (start, end) = gradientPointsForAngle(self.angle)
gradient.startPoint = start
gradient.endPoint = end
}
}
// create gradient layer
private func createGradient() -> CAGradientLayer {
let gradient = CAGradientLayer()
gradient.frame = self.bounds
return gradient
}
// create vector pointing in direction of angle
private func gradientPointsForAngle(_ angle: CGFloat) -> (CGPoint, CGPoint) {
// get vector start and end points
let end = pointForAngle(angle)
//let start = pointForAngle(angle+180.0)
let start = oppositePoint(end)
// convert to gradient space
let p0 = transformToGradientSpace(start)
let p1 = transformToGradientSpace(end)
return (p0, p1)
}
// get a point corresponding to the angle
private func pointForAngle(_ angle: CGFloat) -> CGPoint {
// convert degrees to radians
let radians = angle * .pi / 180.0
var x = cos(radians)
var y = sin(radians)
// (x,y) is in terms unit circle. Extrapolate to unit square to get full vector length
if (fabs(x) > fabs(y)) {
// extrapolate x to unit length
x = x > 0 ? 1 : -1
y = x * tan(radians)
} else {
// extrapolate y to unit length
y = y > 0 ? 1 : -1
x = y / tan(radians)
}
return CGPoint(x: x, y: y)
}
// transform point in unit space to gradient space
private func transformToGradientSpace(_ point: CGPoint) -> CGPoint {
// input point is in signed unit space: (-1,-1) to (1,1)
// convert to gradient space: (0,0) to (1,1), with flipped Y axis
return CGPoint(x: (point.x + 1) * 0.5, y: 1.0 - (point.y + 1) * 0.5)
}
// return the opposite point in the signed unit square
private func oppositePoint(_ point: CGPoint) -> CGPoint {
return CGPoint(x: -point.x, y: -point.y)
}
// ensure the gradient gets initialized when the view is created in IB
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
installGradient()
updateGradient()
}
}
如何以编程方式实现与附加图像中完全相同的渐变?
答案在很大程度上取决于设计师用来创建渐变的内容。有一些工具(例如 Invision)可以为开发人员提供有用的信息,例如渐变颜色和点。但是,如果他们只是在 Photoshop 中创建它,则很难以编程方式重现,也许您应该索取渐变图像。
另一种解决方案是在 Xcode 中使用 @IBDesignable
渐变视图;许多开发人员创建了它们,包括我,主要是因为过去不得不处理很多这个问题。这种方法的一个主要优点是可以非常容易地调整颜色,甚至可以根据需要设置动画。
我有一个开源示例available on GitHub, plus I wrote an extended explanation of how it works。
我快速尝试使用我的项目制作类似您的渐变,这就是它在 Xcode 中的样子:-
关键部分是“属性检查器”面板的顶部,您可以在其中轻松设置开始和结束渐变颜色以及渐变角度 - 您的角度看起来约为 310 度。
下面粘贴了您需要的主要代码。按照上面的链接了解其工作原理。
import UIKit
@IBDesignable
class LDGradientView: UIView {
// the gradient start colour
@IBInspectable var startColor: UIColor? {
didSet {
updateGradient()
}
}
// the gradient end colour
@IBInspectable var endColor: UIColor? {
didSet {
updateGradient()
}
}
// the gradient angle, in degrees anticlockwise from 0 (east/right)
@IBInspectable var angle: CGFloat = 270 {
didSet {
updateGradient()
}
}
// the gradient layer
private var gradient: CAGradientLayer?
// initializers
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
installGradient()
}
override init(frame: CGRect) {
super.init(frame: frame)
installGradient()
}
// Create a gradient and install it on the layer
private func installGradient() {
// if there's already a gradient installed on the layer, remove it
if let gradient = self.gradient {
gradient.removeFromSuperlayer()
}
let gradient = createGradient()
self.layer.addSublayer(gradient)
self.gradient = gradient
}
// Update an existing gradient
private func updateGradient() {
if let gradient = self.gradient {
let startColor = self.startColor ?? UIColor.clear
let endColor = self.endColor ?? UIColor.clear
gradient.colors = [startColor.cgColor, endColor.cgColor]
let (start, end) = gradientPointsForAngle(self.angle)
gradient.startPoint = start
gradient.endPoint = end
}
}
// create gradient layer
private func createGradient() -> CAGradientLayer {
let gradient = CAGradientLayer()
gradient.frame = self.bounds
return gradient
}
// create vector pointing in direction of angle
private func gradientPointsForAngle(_ angle: CGFloat) -> (CGPoint, CGPoint) {
// get vector start and end points
let end = pointForAngle(angle)
//let start = pointForAngle(angle+180.0)
let start = oppositePoint(end)
// convert to gradient space
let p0 = transformToGradientSpace(start)
let p1 = transformToGradientSpace(end)
return (p0, p1)
}
// get a point corresponding to the angle
private func pointForAngle(_ angle: CGFloat) -> CGPoint {
// convert degrees to radians
let radians = angle * .pi / 180.0
var x = cos(radians)
var y = sin(radians)
// (x,y) is in terms unit circle. Extrapolate to unit square to get full vector length
if (fabs(x) > fabs(y)) {
// extrapolate x to unit length
x = x > 0 ? 1 : -1
y = x * tan(radians)
} else {
// extrapolate y to unit length
y = y > 0 ? 1 : -1
x = y / tan(radians)
}
return CGPoint(x: x, y: y)
}
// transform point in unit space to gradient space
private func transformToGradientSpace(_ point: CGPoint) -> CGPoint {
// input point is in signed unit space: (-1,-1) to (1,1)
// convert to gradient space: (0,0) to (1,1), with flipped Y axis
return CGPoint(x: (point.x + 1) * 0.5, y: 1.0 - (point.y + 1) * 0.5)
}
// return the opposite point in the signed unit square
private func oppositePoint(_ point: CGPoint) -> CGPoint {
return CGPoint(x: -point.x, y: -point.y)
}
// ensure the gradient gets initialized when the view is created in IB
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
installGradient()
updateGradient()
}
}