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):

If I change your clearPoint
:
//let clearPoint = CGPoint(x: 1, y: 1)
let clearPoint = CGPoint(x: 0, y: 1) // bottom-left-corner
I get this:

Appearance is the same on Simulator and Device
EDIT
Some additional clarification...
The Radial Gradient doesn't use .startPoint
and .endPoint
in the same way that an .axial
(linear) gradient does.
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.
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
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:

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
:

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
:

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)"
}
}
Everything's done in code - no @IBOutlets
or @IBActions
, so just create a new view controller and assign its Custom Class to GradTestViewController
.