gpt4 book ai didi

ios - 每次出现 View 时,UIView 层的子层显示不同/随机

转载 作者:行者123 更新时间:2023-11-28 06:43:59 27 4
gpt4 key购买 nike

我有一个简单的自定义 CALayer,可以在我的 UIView 上创建叠加渐变效果。这是代码:

class GradientLayer: CALayer {

var locations: [CGFloat]?
var origin: CGPoint?
var radius: CGFloat?
var color: CGColor?

convenience init(view: UIView, locations: [CGFloat]?, origin: CGPoint?, radius: CGFloat?, color: UIColor?) {
self.init()
self.locations = locations
self.origin = origin
self.radius = radius
self.color = color?.CGColor
self.frame = view.bounds
}

override func drawInContext(ctx: CGContext) {
super.drawInContext(ctx)

guard let locations = self.locations else { return }
guard let origin = self.origin else { return }
guard let radius = self.radius else { return }

let colorSpace = CGColorGetColorSpace(color)
let colorComponents = CGColorGetComponents(color)

let gradient = CGGradientCreateWithColorComponents(colorSpace, colorComponents, locations, locations.count)
CGContextDrawRadialGradient(ctx, gradient, origin, CGFloat(0), origin, radius, [.DrawsAfterEndLocation])
}
}

我在这里初始化并设置这些层:

override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let gradient1 = GradientLayer(view: view, locations: [0.0,1.0], origin: CGPoint(x: view.frame.midX, y: view.frame.midY), radius: 100.0, color: UIColor(white: 1.0, alpha: 0.2))

let gradient2 = GradientLayer(view: view, locations: [0.0,1.0], origin: CGPoint(x: view.frame.midX-20, y: view.frame.midY+20), radius: 160.0, color: UIColor(white: 1.0, alpha: 0.2))

let gradient3 = GradientLayer(view: view, locations: [0.0,1.0], origin: CGPoint(x: view.frame.midX+30, y: view.frame.midY-30), radius: 300.0, color: UIColor(white: 1.0, alpha: 0.2))

gradient1.setNeedsDisplay()
gradient2.setNeedsDisplay()
gradient3.setNeedsDisplay()

view.layer.addSublayer(gradient1)
view.layer.addSublayer(gradient2)
view.layer.addSublayer(gradient3)
}

该 View 似乎在大多数时间都能正确显示,但(看似)随机我会得到不同的渲染,如下所示。下面是一些示例(第一个是我想要的):

enter image description here enter image description here enter image description here

导致此故障的原因是什么?如何每次只加载第一个?

最佳答案

你有几个问题。

首先,您应该将渐变视为一组stops,其中stop 有两部分:颜色和位置。您必须具有相同数量的颜色和位置,因为每个站点都有一个。例如,如果您检查 CGGradientCreateWithColorComponents,就可以看到这一点。有关 components 的文档参数:

The number of items in this array should be the product of count and the number of components in the color space.

它是一个乘积(乘法的结果),因为你有 count停靠点,您需要为每个停靠点提供一套完整的颜色组件。

您没有提供足够的颜色组件。你的GradientLayer可以有任意数量的位置(你给它两个)但只有一种颜色。您正在获取一种颜色的成分并将其作为成分数组传递给 CGGradientCreateWithColorComponents , 但数组太短。 Swift 不会捕捉到这个错误——注意你的 colorComponents 的类型是UnsafePointer<CGFloat> . Unsafe部分告诉您您处于危险区域。 (您可以通过在 Xcode 中单击选项来查看 colorComponents 的类型。)

因为您没有为 components 提供足够大的数组,iOS 正在使用你的一种颜色的组件之后恰好在内存中的任何随机值。这些可能会随着运行而变化,并且通常不是您想要的。

事实上,你甚至不应该使用 CGGradientCreateWithColorComponents .你应该使用 CGGradientCreateWithColors , 它采用 CGColor 的数组所以它不仅使用起来更简单,而且更安全,因为它少了一个 UnsafePointer飘来飘去。

这是GradientLayer应该看起来像:

class RadialGradientLayer: CALayer {

struct Stop {
var location: CGFloat
var color: UIColor
}

var stops: [Stop] { didSet { self.setNeedsDisplay() } }
var origin: CGPoint { didSet { self.setNeedsDisplay() } }
var radius: CGFloat { didSet { self.setNeedsDisplay() } }

init(stops: [Stop], origin: CGPoint, radius: CGFloat) {
self.stops = stops
self.origin = origin
self.radius = radius
super.init()
needsDisplayOnBoundsChange = true
}

override init(layer other: AnyObject) {
guard let other = other as? RadialGradientLayer else { fatalError() }
stops = other.stops
origin = other.origin
radius = other.radius
super.init(layer: other)
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func drawInContext(ctx: CGContext) {
let locations = stops.map { $0.location }
let colors = stops.map { $0.color.CGColor }

locations.withUnsafeBufferPointer { pointer in
let gradient = CGGradientCreateWithColors(nil, colors, pointer.baseAddress)
CGContextDrawRadialGradient(ctx, gradient, origin, 0, origin, radius, [.DrawsAfterEndLocation])
}
}

}

下一个问题。每次系统调用 viewWillLayoutSubviews 时,您都会添加更多渐变层。 .它可以多次调用该函数!例如,如果您的应用程序支持界面旋转,或者如果有电话进来并且 iOS 使状态栏变高,它就会调用它。 (您可以通过选择 Hardware > Toggle In-Call Status Bar 在模拟器中测试它。)

您需要创建一次渐变层,并将它们存储在一个属性中。如果它们已经创建,您需要更新它们的框架而不是创建新层:

class ViewController: UIViewController {

private var gradientLayers = [RadialGradientLayer]()

override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()

if gradientLayers.isEmpty {
createGradientLayers()
}

for layer in gradientLayers {
layer.frame = view.bounds
}
}

private func createGradientLayers() {
let bounds = view.bounds
let mid = CGPointMake(bounds.midX, bounds.midY)
typealias Stop = RadialGradientLayer.Stop

for (point, radius, color) in [
(mid, 100, UIColor(white:1, alpha:0.2)),
(CGPointMake(mid.x - 20, mid.y + 20), 160, UIColor(white:1, alpha:0.2)),
(CGPointMake(mid.x + 30, mid.y - 30), 300, UIColor(white:1, alpha:0.2))
] as [(CGPoint, CGFloat, UIColor)] {

let stops: [RadialGradientLayer.Stop] = [
Stop(location: 0, color: color),
Stop(location: 1, color: color.colorWithAlphaComponent(0))]

let layer = RadialGradientLayer(stops: stops, origin: point, radius: radius)
view.layer.addSublayer(layer)
gradientLayers.append(layer)
}
}

}

关于ios - 每次出现 View 时,UIView 层的子层显示不同/随机,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37082383/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com