我正在尝试在swift中创建一个径向CAGradientLayer。正常的CAGradientLayers (默认类型为axial)是没有问题的。它们在快照和模拟器中的运行时都渲染得很好。
当我为我创建的(仅用于CAGradientLayer)的UIView拍摄快照时,它显示了一个漂亮的径向CAGradientLayer。然而,当我在模拟器中使用这个视图并在运行时查看它时,它看起来很糟糕。我不知道我做错了什么。
这是CAGradientLayer的代码:
import Foundation
class ChannelGradientView: UIView {
// MARK: - Init
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
// MARK: - Setup
private func setup() {
backgroundColor = .clear
setupGradient()
}
private func setupGradient() {
let gradient = layer as? CAGradientLayer
gradient?.type = .radial
gradient?.colors = [
UIColor.black.withAlphaComponent(0.8),
UIColor.black.withAlphaComponent(0)
].map { $0.cgColor }
let blackPoint = CGPoint(x: 1, y: 0)
let clearPoint = CGPoint(x: 1, y: 1)
// startpoint is center (for example black here)
gradient?.startPoint = blackPoint
gradient?.endPoint = clearPoint
}
// MARK: - Layer
override public class var layerClass: Swift.AnyClass {
return CAGradientLayer.self
}
}这是我进行快照单元测试时的样子(使用Nimble snapshot库):

这是它在模拟器上运行时的样子:


有谁知道我做错了什么吗?
编辑:在实际的设备上运行时,它甚至不会显示任何渐变层,甚至不会显示糟糕的渐变层。这是个透明的盒子。
发布于 2019-09-11 21:36:27
不确定在模拟器和设备上看到的东西之间可能还发生了什么,但是...
如果我按原样使用你的代码,我会得到这样的结果(显示框架的红色边框):

如果我更改了你的clearPoint
//let clearPoint = CGPoint(x: 1, y: 1)
let clearPoint = CGPoint(x: 0, y: 1) // bottom-left-corner我明白了:

模拟器和设备上的外观相同
编辑
一些额外的澄清。
径向渐变与.axial (线性)渐变使用.startPoint和.endPoint的方式不同。
使用.radial绘制一个渐变的*椭圆,以.startPoint为中心,startPoint.x和endPoint.x的差值乘以2作为其宽度,startPoint.y和endPoint.y的差值乘以2作为其高度。
因此,要获得所需的从右上角到左下角的径向渐变,需要将.startPoint设置为1,0,将.endPoint设置为导致渐变椭圆大小为2 x 2的值
startPoint = 1,0
endPoint = 0,1
width: abs(1 - 0) * 2 = 2
height: abs(0 - 1) * 2 = 2请注意,您可以使用以下命令实现相同的结果:
startPoint = 1,0
endPoint = 2,-1
width: abs(1 - 2) * 2 = 2
height: abs(0 - (-1)) * 2 = 2.radial gradient 不喜欢的是将其宽度或高度设置为零,这就是我们得到的结果:
startPoint = 1,0
endPoint = 1,1
width: abs(1 - 1) * 2 = 0 // problem!!!!
height: abs(0 - 1) * 2 = 2正如我们已经看到的,结果是奇怪的线条模式。
这里有一些实际的例子来演示。
轴/线性从右上角到左下角:

径向居中,宽度=视图宽度/高度=视图高度:

径向居中和宽度=一半视图宽度/高度=视图高度:

径向居中和宽度=一半视图宽度/高度=视图高度...结果完全相同,但请注意,.endPoint.x值是0.75而不是0.25

现在我们更改了.startPoint.x = 0.75,但保留了.endPoint.x = 0.25,因此椭圆的中心移动到视图宽度的四分之三,但是椭圆的宽度等于视图的宽度……abs(0.75 - 0.25) * 2 == 1.0

更改.endPoint.x = 0.5,宽度将恢复为视图宽度的1/2 ...abs(0.75 - 0.5) * 2 = 0.5

最后是从右上到左下的径向渐变:

这是我用来生成这些图像的代码。它有一个“渐变定义”的数据块...您可以添加/更改这些定义来尝试这些差异。
//
// GradTestViewController.swift
//
// Created by Don Mag on 9/12/19.
//
import UIKit
class TestGradientView: UIView {
// MARK: - Init
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
// MARK: - Setup
private func setup() {
// just set the background to clear and the
// gradient colors to blue -> green
backgroundColor = .clear
if let gradient = layer as? CAGradientLayer {
gradient.colors = [
UIColor.blue,
UIColor.green,
].map { $0.cgColor }
}
}
// MARK: - Layer
override public class var layerClass: Swift.AnyClass {
return CAGradientLayer.self
}
}
struct GradDef {
var gradType: CAGradientLayerType = .axial
var startPoint: CGPoint = CGPoint(x: 1.0, y: 0.0)
var endPoint: CGPoint = CGPoint(x: 0.0, y: 1.0)
}
class GradTestViewController: UIViewController {
var theButton: UIButton = {
let v = UIButton()
v.setTitle("Tap", for: .normal)
v.setTitleColor(.white, for: .normal)
v.setTitleColor(.lightGray, for: .highlighted)
v.backgroundColor = .red
return v
}()
var counterLabel: UILabel = {
let v = UILabel()
return v
}()
var descLabel: UILabel = {
let v = UILabel()
v.numberOfLines = 0
return v
}()
var gradContainerView: UIView = {
let v = UIView()
return v
}()
var gradView: TestGradientView = {
let v = TestGradientView()
return v
}()
var tlLabel: UILabel = {
let v = UILabel()
v.text = "0,0"
return v
}()
var trLabel: UILabel = {
let v = UILabel()
v.text = "1,0"
return v
}()
var blLabel: UILabel = {
let v = UILabel()
v.text = "0,1"
return v
}()
var brLabel: UILabel = {
let v = UILabel()
v.text = "1,1"
return v
}()
var theStackView: UIStackView = {
let v = UIStackView()
v.axis = .vertical
v.alignment = .center
v.distribution = .fill
v.spacing = 20.0
return v
}()
var gradDefs: [GradDef] = [
GradDef(gradType: .axial, startPoint: CGPoint(x: 1.0, y: 0.0), endPoint: CGPoint(x: 0.0, y: 1.0)),
GradDef(gradType: .radial, startPoint: CGPoint(x: 0.5, y: 0.5), endPoint: CGPoint(x: 0.0, y: 0.0)),
GradDef(gradType: .radial, startPoint: CGPoint(x: 0.5, y: 0.5), endPoint: CGPoint(x: 0.25, y: 0.0)),
GradDef(gradType: .radial, startPoint: CGPoint(x: 0.5, y: 0.5), endPoint: CGPoint(x: 0.75, y: 0.0)),
GradDef(gradType: .radial, startPoint: CGPoint(x: 0.75, y: 0.5), endPoint: CGPoint(x: 0.25, y: 0.0)),
GradDef(gradType: .radial, startPoint: CGPoint(x: 0.75, y: 0.5), endPoint: CGPoint(x: 1.0, y: 0.0)),
GradDef(gradType: .radial, startPoint: CGPoint(x: 1.0, y: 0.0), endPoint: CGPoint(x: 0.0, y: 1.0)),
]
var idx: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
[theButton, counterLabel, gradContainerView, gradView, tlLabel, trLabel, blLabel, brLabel, descLabel, theStackView].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
}
[theButton, counterLabel, gradContainerView, descLabel].forEach {
theStackView.addArrangedSubview($0)
}
[gradView, tlLabel, trLabel, blLabel, brLabel].forEach {
gradContainerView.addSubview($0)
}
[counterLabel, tlLabel, trLabel, blLabel, brLabel, descLabel].forEach {
$0.font = UIFont.monospacedDigitSystemFont(ofSize: 14.0, weight: .regular)
}
NSLayoutConstraint.activate([
gradView.widthAnchor.constraint(equalToConstant: 120),
gradView.heightAnchor.constraint(equalTo: gradView.widthAnchor),
gradView.centerXAnchor.constraint(equalTo: gradContainerView.centerXAnchor),
gradView.centerYAnchor.constraint(equalTo: gradContainerView.centerYAnchor),
tlLabel.centerXAnchor.constraint(equalTo: gradView.leadingAnchor, constant: 0.0),
blLabel.centerXAnchor.constraint(equalTo: gradView.leadingAnchor, constant: 0.0),
trLabel.centerXAnchor.constraint(equalTo: gradView.trailingAnchor, constant: 0.0),
brLabel.centerXAnchor.constraint(equalTo: gradView.trailingAnchor, constant: 0.0),
tlLabel.bottomAnchor.constraint(equalTo: gradView.topAnchor, constant: -2.0),
trLabel.bottomAnchor.constraint(equalTo: gradView.topAnchor, constant: -2.0),
blLabel.topAnchor.constraint(equalTo: gradView.bottomAnchor, constant: 2.0),
brLabel.topAnchor.constraint(equalTo: gradView.bottomAnchor, constant: 2.0),
tlLabel.topAnchor.constraint(equalTo: gradContainerView.topAnchor, constant: 4.0),
tlLabel.leadingAnchor.constraint(equalTo: gradContainerView.leadingAnchor, constant: 4.0),
brLabel.trailingAnchor.constraint(equalTo: gradContainerView.trailingAnchor, constant: -4.0),
brLabel.bottomAnchor.constraint(equalTo: gradContainerView.bottomAnchor, constant: -4.0),
])
view.addSubview(theStackView)
NSLayoutConstraint.activate([
theStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40.0),
theStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
theButton.widthAnchor.constraint(equalToConstant: 160.0),
descLabel.widthAnchor.constraint(equalToConstant: 240.0),
])
theButton.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)
idx = -1
didTap(nil)
}
@IBAction func didTap(_ sender: Any?) {
guard let gLayer = gradView.layer as? CAGradientLayer else {
fatalError("Could not get the gradient layer!")
}
idx += 1
if idx >= gradDefs.count {
idx = 0
}
let gDef = gradDefs[idx]
gLayer.type = gDef.gradType
gLayer.startPoint = gDef.startPoint
gLayer.endPoint = gDef.endPoint
var s = ""
s += "Gradient Type: " + (gDef.gradType == CAGradientLayerType.axial ? "Axial" : "Radial")
s += "\n\n"
s += "Start Point: \(gDef.startPoint)"
s += "\n"
s += "End Point: \(gDef.endPoint)"
if gDef.gradType == CAGradientLayerType.radial {
let w = abs(gDef.startPoint.x - gDef.endPoint.x) * 2
let h = abs(gDef.startPoint.y - gDef.endPoint.y) * 2
s += "\n\n"
s += "\t" + "Radial Width:"
s += "\n"
s += "\t\t" + "abs(\(gDef.startPoint.x) - \(gDef.endPoint.x)) * 2 == \(w)"
s += "\n\n"
s += "\t" + "Radial Height:"
s += "\n"
s += "\t\t" + "abs(\(gDef.startPoint.y) - \(gDef.endPoint.y)) * 2 == \(h)"
}
s += "\n"
descLabel.text = s
counterLabel.text = "Variation \(idx + 1) of \(gradDefs.count)"
}
}一切都在代码中完成-没有@IBOutlets或@IBActions,所以只需创建一个新的视图控制器并将其自定义类分配给GradTestViewController即可。
发布于 2019-09-12 00:27:23
这是修复方法,如下所示设置您的点:
let blackPoint = CGPoint(x: 1.0, y: 0.0)
let clearPoint = CGPoint(x: 0.0, y: 1.0)告诉我在你的环境下是否可以。
--
我将你的整个代码放在一个游乐场中,如下所示,我做了一些调整(我把渐变放在右上角):
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class ChannelGradientView: UIView {
// MARK: - Init
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
// MARK: - Setup
private func setup() {
backgroundColor = .clear
setupGradient()
}
private func setupGradient() {
guard let gradient = layer as? CAGradientLayer else { return }
//let gradient = CAGradientLayer()
gradient.type = .radial
gradient.frame = frame
gradient.colors = [
UIColor.black.cgColor,
UIColor.clear.cgColor
]
let blackPoint = CGPoint(x: 1.0, y: 0.0)
let clearPoint = CGPoint(x: 0.0, y: 1.0)
// startpoint is center (for example black here)
gradient.startPoint = blackPoint
gradient.endPoint = clearPoint
}
// MARK: - Layer
override public class var layerClass: Swift.AnyClass {
return CAGradientLayer.self
}
}
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
self.view = view
view.backgroundColor = .white
let frame = CGRect(x: 150, y: 200, width: 200, height: 200)
let iv = UIImageView()
iv.frame = frame
iv.alpha = 0.8
iv.contentMode = .scaleAspectFill
if let url = URL(string: "https://images-na.ssl-images-amazon.com/images/I/61VpNkHPRoL._SX679_.jpg"),
let img = try? Data(contentsOf: url) {
iv.image = UIImage(data: img)
}
view.addSubview(iv)
iv.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
iv.leadingAnchor.constraint(equalTo: view.leadingAnchor),
iv.trailingAnchor.constraint(equalTo: view.trailingAnchor),
iv.topAnchor.constraint(equalTo: view.topAnchor),
iv.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
let gd = ChannelGradientView()
view.addSubview(gd)
gd.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
gd.trailingAnchor.constraint(equalTo: view.trailingAnchor),
gd.topAnchor.constraint(equalTo: view.topAnchor),
gd.widthAnchor.constraint(equalTo: view.widthAnchor),
gd.heightAnchor.constraint(equalTo: gd.widthAnchor),
])
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()并且它确实可以正确显示:

https://stackoverflow.com/questions/57889427
复制相似问题