径向CAGradientLayer在运行时无法正确呈现 [英] Radial CAGradientLayer not rendering properly at runtime

查看:133
本文介绍了径向CAGradientLayer在运行时无法正确呈现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试快速创建一个径向CAGradientLayer.普通的CAGradientLayers(默认类型为axial)没有问题.它们可以在快照和模拟器的运行时上很好地呈现.

I'm trying to create a radial CAGradientLayer in swift. Normal CAGradientLayers (with default type axial) are no problem. They are rendering fine on the snapshot and on runtime in the simulator.

当我为创建的UIView拍摄快照时(仅用于CAGradientLayer),它显示了一个漂亮的径向CAGradientLayer.但是,当我使用此视图并在模拟器中的运行时对其进行查看时,它看起来很糟糕.我不知道我在做什么错.

When I take a snapshot of the UIView that I created (solely for the CAGradientLayer), it shows a beautiful radial CAGradientLayer. However, when I use this view and look at it at runtime in the simulator, it looks awful. I don't know what I'm doing wrong.

这是CAGradientLayer的代码:

This is the code for the 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库)时的样子:

This is what it looks like when I take a snapshot unit test (with Nimble Snapshot library):

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

And this is what it looks like at runtime on a simulator:

有人知道我在做什么错吗?

Anyone that has an idea of what I'm doing wrong?

在实际设备上运行,它甚至不显示任何渐变层,甚至不显示任何渐变层.这是一个透明的盒子.

Running at an actual device, it doesn't even show any gradient layer, not even the crappy one. It's a clear box.

推荐答案

不确定在模拟器与设备上看到的内容之间可能还会发生什么,但是...

Not sure what else might be going on between what you see on Simulator vs Device, but...

如果我照原样使用您的代码,则会收到此消息(红色边框显示框架):

If I use your code as-is, I get this (red border to show the frame):

如果我更改您的clearPoint:

//let clearPoint = CGPoint(x: 1, y: 1)
let clearPoint = CGPoint(x: 0, y: 1)    // bottom-left-corner

我明白了:

模拟器和设备上的外观相同

Appearance is the same on Simulator and Device

编辑

一些其他说明...

径向渐变与.axial(线性)渐变使用的方式不同,不会使用.startPoint.endPoint.

The Radial Gradient doesn't use .startPoint and .endPoint in the same way that an .axial (linear) gradient does.

使用.radial,以.startPoint为中心绘制渐变 *椭圆,将startPoint.xendPoint.x之间的差乘以2作为宽度,以及startPoint.yendPoint.y将其高度乘以2.

With .radial, a gradient *ellipse is drawn, using .startPoint as its center, and the difference between startPoint.x and endPoint.x times 2 as its width and the difference between startPoint.y and endPoint.y times 2 as its height.

因此,要获得所需的从右上到左下的径向渐变,需要将.startPoint设置为1,0并将.endPoint设置为导致大小为2 x 2的渐变椭圆的值:

So, to get the top-right to bottom-left radial gradient you want, you need to set .startPoint to 1,0 and the .endPoint to values which result in a gradient oval of size 2 x 2:

startPoint = 1,0

endPoint = 0,1

    width:  abs(1 - 0) * 2 = 2
    height: abs(0 - 1) * 2 = 2

请注意,您可以通过以下方式获得相同的结果:

note that you can achieve the same result with:

startPoint = 1,0

endPoint = 2,-1

    width:  abs(1 - 2) * 2 = 2
    height: abs(0 - (-1)) * 2 = 2

.radial渐变 所喜欢的是将其宽度或高度设置为零,这就是我们得到的:

What the .radial gradient doesn't like is to have its width or height set to Zero, which is what we got with:

startPoint = 1,0

endPoint = 1,1

    width:  abs(1 - 1) * 2 = 0   // problem!!!!
    height: abs(0 - 1) * 2 = 2

我们已经看到,结果是奇怪的线条图案.

The result, as we've seen, is the weird line pattern.

这里有一些实际的例子来演示.

Here are some practical examples to demonstrate.

轴向/线性从右上到左下:

Axial / Linear top-right to bottom-left:

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

Radial centered and width = view width / height = view height:

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

Radial centered and width = one-half view width / height = view height:

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

Radial centered and width = one-half view width / height = view height... Exact same result, but note that the .endPoint.x value is 0.75 instead of 0.25:

现在,我们更改.startPoint.x = 0.75,但保留.endPoint.x = 0.25,因此椭圆的中心移至视图宽度的3/4,但椭圆的宽度变得等于视图的宽度. .. abs(0.75 - 0.25) * 2 == 1.0:

Now we change .startPoint.x = 0.75, but we leave .endPoint.x = 0.25, so the center of the ellipse moves to 3/4ths of the width of the view, but the width of the ellipse becomes equal to the width of the view... abs(0.75 - 0.25) * 2 == 1.0:

更改.endPoint.x = 0.5,宽度返回到视图宽度的1/2 ... abs(0.75 - 0.5) * 2 = 0.5:

Change .endPoint.x = 0.5 and the width returns to 1/2 the width of the view... abs(0.75 - 0.5) * 2 = 0.5:

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

And finally radial gradient from top-right to bottom-left:

这是我用来生成这些图像的代码.它具有梯度定义"的数据块...您可以添加/更改这些定义以尝试差异.

Here is the code I used to generate these images. It has a data-block of "Gradient Definitions"... You can add / change those definitions to experiment with the differences.

//
//  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.

Everything's done in code - no @IBOutlets or @IBActions, so just create a new view controller and assign its Custom Class to GradTestViewController.

这篇关于径向CAGradientLayer在运行时无法正确呈现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆