UITextView 渐变层应用不起作用
UITextView gradient layer apply not working
我想在 UITextView 的顶部 10% 和底部 10% 上应用渐变图层。为此,我放置了一个名为容器视图的虚拟 UIView,并使 UITextView 成为它的子视图。然后我添加以下代码:
if let containerView = textView.superview {
let gradient = CAGradientLayer(layer: containerView.layer)
gradient.frame = containerView.bounds
gradient.colors = [UIColor.clear.cgColor, UIColor.black.cgColor]
gradient.locations = [0.0, 0.1, 0.9, 1.0]
containerView.layer.mask = gradient
}
但是渐变只应用到顶部,而不是底部。代码有问题吗?
此外,如果我随时通过修改它的约束来调整容器视图的大小,我是否需要每次都编辑遮罩层?
编辑:这是@DonMag 回答的输出。
但我想要的是像这张图片那样的文字在底部淡化的东西。
编辑 2:
以下是 DonMag 修改答案后的截图。
编辑 - 澄清所需效果后...
关于为什么您只看到顶部看台上的渐变,我的初步回答是:
You're only seeing the gradient on the top because you gave it four locations but only two colors.
那么,既然您提供了您正在尝试做的事情的图像...
使用此 DoubleGradientMaskView
作为文本视图的“容器”视图:
class DoubleGradientMaskView: UIView {
let gradientLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
gradientLayer.colors = [UIColor.clear.cgColor, UIColor.black.cgColor, UIColor.black.cgColor, UIColor.clear.cgColor]
gradientLayer.locations = [0.0, 0.1, 0.9, 1.0]
layer.mask = gradientLayer
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = bounds
}
}
示例控制器:
class GradientTextViewViewController: UIViewController {
let textView = UITextView()
let containerView = DoubleGradientMaskView()
let bkgImageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
[bkgImageView, textView, containerView].forEach {
[=11=].translatesAutoresizingMaskIntoConstraints = false
}
bkgImageView.contentMode = .scaleAspectFill
if let img = UIImage(named: "background") {
bkgImageView.image = img
} else {
bkgImageView.backgroundColor = .blue
}
view.addSubview(bkgImageView)
view.addSubview(containerView)
containerView.addSubview(textView)
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// add an image view so we can see the white text
bkgImageView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
bkgImageView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
bkgImageView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
bkgImageView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
// constraint text view inside container
textView.topAnchor.constraint(equalTo: containerView.topAnchor),
textView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
textView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
textView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
// constrain container Top / Bottom 40, Leading / Trailing 40
containerView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
containerView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
containerView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
containerView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -40.0),
])
textView.isScrollEnabled = true
textView.font = UIFont.systemFont(ofSize: 48.0, weight: .bold)
textView.textColor = .white
textView.backgroundColor = .clear
textView.text = String((1...20).flatMap { "This is row \([=11=])\n" })
}
}
结果:
或者,使用蓝色背景而不是图像:
你只在顶部看到渐变,因为你给了它四个个位置,但只有两种种颜色。
将颜色更改为:
gradient.colors = [UIColor.clear.cgColor, UIColor.black.cgColor, UIColor.black.cgColor, UIColor.clear.cgColor]
可能会给你想要的外观...但你需要额外的代码来处理尺寸变化。
如果您使用此 class 作为您的“容器”视图,将自动调整大小:
class DoubleGradientView: UIView {
var gradientLayer: CAGradientLayer!
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
gradientLayer = self.layer as? CAGradientLayer
gradientLayer.colors = [UIColor.black.cgColor, UIColor.clear.cgColor, UIColor.clear.cgColor, UIColor.black.cgColor]
gradientLayer.locations = [0.0, 0.1, 0.9, 1.0]
}
}
这是一个示例控制器。它在容器中创建了两个“文本视图”。
- 最上面的是可滚动的,高度为100。
- 底部的不可滚动,因此它会在您键入时调整其高度以适应文本。
两者都被限制在 60 点的前导/尾随,因此当您旋转设备时,您还会看到自动渐变更新。
class GradientBehindTextViewViewController: UIViewController {
let textView1 = UITextView()
let containerView1 = DoubleGradientView()
let textView2 = UITextView()
let containerView2 = DoubleGradientView()
override func viewDidLoad() {
super.viewDidLoad()
[textView1, containerView1, textView2, containerView2].forEach {
[=14=].translatesAutoresizingMaskIntoConstraints = false
}
containerView1.addSubview(textView1)
view.addSubview(containerView1)
containerView2.addSubview(textView2)
view.addSubview(containerView2)
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// constraint text view inside container
textView1.topAnchor.constraint(equalTo: containerView1.topAnchor),
textView1.leadingAnchor.constraint(equalTo: containerView1.leadingAnchor),
textView1.trailingAnchor.constraint(equalTo: containerView1.trailingAnchor),
textView1.bottomAnchor.constraint(equalTo: containerView1.bottomAnchor),
// constrain container Top + 40, Leading / Trailing 80
containerView1.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
containerView1.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 80.0),
containerView1.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -80.0),
// text view 1 will have scrolling enabled, so we'll set its height to 100
containerView1.heightAnchor.constraint(equalToConstant: 100.0),
// constraint text view inside container
textView2.topAnchor.constraint(equalTo: containerView2.topAnchor),
textView2.leadingAnchor.constraint(equalTo: containerView2.leadingAnchor),
textView2.trailingAnchor.constraint(equalTo: containerView2.trailingAnchor),
textView2.bottomAnchor.constraint(equalTo: containerView2.bottomAnchor),
// constrain container2 Top to container1 bottom + 40, Leading / Trailing 80
containerView2.topAnchor.constraint(equalTo: containerView1.bottomAnchor, constant: 40.0),
containerView2.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 80.0),
containerView2.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -80.0),
// text view 2 will NOT scroll (it will size with the text) so no height / bottom
])
// text view 1 should scroll
textView1.isScrollEnabled = true
// text view 1 should NOT scroll we want the text view to size itelf as we type
textView2.isScrollEnabled = false
// let the gradient show through
textView1.backgroundColor = .clear
textView2.backgroundColor = .clear
textView1.text = "Initial text for text view 1."
textView2.text = "Initial text for text view 2."
}
}
@DongMag 解决方案很复杂。相反,您只需要一个像这样实现的掩码:
@IBDesignable
class MaskableLabel: UILabel {
var maskImageView = UIImageView()
@IBInspectable
var maskImage: UIImage? {
didSet {
maskImageView.image = maskImage
updateView()
}
}
override func layoutSubviews() {
super.layoutSubviews()
updateView()
}
func updateView() {
if maskImageView.image != nil {
maskImageView.frame = bounds
mask = maskImageView
}
}
}
然后用一个简单的渐变遮罩like this,你甚至可以在故事板中看到它。
注意:您可以使用此方法并将UILabel
替换为您想要子类化的任何其他视图。
我想在 UITextView 的顶部 10% 和底部 10% 上应用渐变图层。为此,我放置了一个名为容器视图的虚拟 UIView,并使 UITextView 成为它的子视图。然后我添加以下代码:
if let containerView = textView.superview {
let gradient = CAGradientLayer(layer: containerView.layer)
gradient.frame = containerView.bounds
gradient.colors = [UIColor.clear.cgColor, UIColor.black.cgColor]
gradient.locations = [0.0, 0.1, 0.9, 1.0]
containerView.layer.mask = gradient
}
但是渐变只应用到顶部,而不是底部。代码有问题吗?
此外,如果我随时通过修改它的约束来调整容器视图的大小,我是否需要每次都编辑遮罩层?
编辑:这是@DonMag 回答的输出。
但我想要的是像这张图片那样的文字在底部淡化的东西。
编辑 2:
以下是 DonMag 修改答案后的截图。
编辑 - 澄清所需效果后...
关于为什么您只看到顶部看台上的渐变,我的初步回答是:
You're only seeing the gradient on the top because you gave it four locations but only two colors.
那么,既然您提供了您正在尝试做的事情的图像...
使用此 DoubleGradientMaskView
作为文本视图的“容器”视图:
class DoubleGradientMaskView: UIView {
let gradientLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
gradientLayer.colors = [UIColor.clear.cgColor, UIColor.black.cgColor, UIColor.black.cgColor, UIColor.clear.cgColor]
gradientLayer.locations = [0.0, 0.1, 0.9, 1.0]
layer.mask = gradientLayer
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = bounds
}
}
示例控制器:
class GradientTextViewViewController: UIViewController {
let textView = UITextView()
let containerView = DoubleGradientMaskView()
let bkgImageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
[bkgImageView, textView, containerView].forEach {
[=11=].translatesAutoresizingMaskIntoConstraints = false
}
bkgImageView.contentMode = .scaleAspectFill
if let img = UIImage(named: "background") {
bkgImageView.image = img
} else {
bkgImageView.backgroundColor = .blue
}
view.addSubview(bkgImageView)
view.addSubview(containerView)
containerView.addSubview(textView)
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// add an image view so we can see the white text
bkgImageView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
bkgImageView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
bkgImageView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
bkgImageView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
// constraint text view inside container
textView.topAnchor.constraint(equalTo: containerView.topAnchor),
textView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
textView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
textView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
// constrain container Top / Bottom 40, Leading / Trailing 40
containerView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
containerView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
containerView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
containerView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -40.0),
])
textView.isScrollEnabled = true
textView.font = UIFont.systemFont(ofSize: 48.0, weight: .bold)
textView.textColor = .white
textView.backgroundColor = .clear
textView.text = String((1...20).flatMap { "This is row \([=11=])\n" })
}
}
结果:
或者,使用蓝色背景而不是图像:
你只在顶部看到渐变,因为你给了它四个个位置,但只有两种种颜色。
将颜色更改为:
gradient.colors = [UIColor.clear.cgColor, UIColor.black.cgColor, UIColor.black.cgColor, UIColor.clear.cgColor]
可能会给你想要的外观...但你需要额外的代码来处理尺寸变化。
如果您使用此 class 作为您的“容器”视图,将自动调整大小:
class DoubleGradientView: UIView {
var gradientLayer: CAGradientLayer!
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
gradientLayer = self.layer as? CAGradientLayer
gradientLayer.colors = [UIColor.black.cgColor, UIColor.clear.cgColor, UIColor.clear.cgColor, UIColor.black.cgColor]
gradientLayer.locations = [0.0, 0.1, 0.9, 1.0]
}
}
这是一个示例控制器。它在容器中创建了两个“文本视图”。
- 最上面的是可滚动的,高度为100。
- 底部的不可滚动,因此它会在您键入时调整其高度以适应文本。
两者都被限制在 60 点的前导/尾随,因此当您旋转设备时,您还会看到自动渐变更新。
class GradientBehindTextViewViewController: UIViewController {
let textView1 = UITextView()
let containerView1 = DoubleGradientView()
let textView2 = UITextView()
let containerView2 = DoubleGradientView()
override func viewDidLoad() {
super.viewDidLoad()
[textView1, containerView1, textView2, containerView2].forEach {
[=14=].translatesAutoresizingMaskIntoConstraints = false
}
containerView1.addSubview(textView1)
view.addSubview(containerView1)
containerView2.addSubview(textView2)
view.addSubview(containerView2)
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// constraint text view inside container
textView1.topAnchor.constraint(equalTo: containerView1.topAnchor),
textView1.leadingAnchor.constraint(equalTo: containerView1.leadingAnchor),
textView1.trailingAnchor.constraint(equalTo: containerView1.trailingAnchor),
textView1.bottomAnchor.constraint(equalTo: containerView1.bottomAnchor),
// constrain container Top + 40, Leading / Trailing 80
containerView1.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
containerView1.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 80.0),
containerView1.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -80.0),
// text view 1 will have scrolling enabled, so we'll set its height to 100
containerView1.heightAnchor.constraint(equalToConstant: 100.0),
// constraint text view inside container
textView2.topAnchor.constraint(equalTo: containerView2.topAnchor),
textView2.leadingAnchor.constraint(equalTo: containerView2.leadingAnchor),
textView2.trailingAnchor.constraint(equalTo: containerView2.trailingAnchor),
textView2.bottomAnchor.constraint(equalTo: containerView2.bottomAnchor),
// constrain container2 Top to container1 bottom + 40, Leading / Trailing 80
containerView2.topAnchor.constraint(equalTo: containerView1.bottomAnchor, constant: 40.0),
containerView2.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 80.0),
containerView2.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -80.0),
// text view 2 will NOT scroll (it will size with the text) so no height / bottom
])
// text view 1 should scroll
textView1.isScrollEnabled = true
// text view 1 should NOT scroll we want the text view to size itelf as we type
textView2.isScrollEnabled = false
// let the gradient show through
textView1.backgroundColor = .clear
textView2.backgroundColor = .clear
textView1.text = "Initial text for text view 1."
textView2.text = "Initial text for text view 2."
}
}
@DongMag 解决方案很复杂。相反,您只需要一个像这样实现的掩码:
@IBDesignable
class MaskableLabel: UILabel {
var maskImageView = UIImageView()
@IBInspectable
var maskImage: UIImage? {
didSet {
maskImageView.image = maskImage
updateView()
}
}
override func layoutSubviews() {
super.layoutSubviews()
updateView()
}
func updateView() {
if maskImageView.image != nil {
maskImageView.frame = bounds
mask = maskImageView
}
}
}
然后用一个简单的渐变遮罩like this,你甚至可以在故事板中看到它。
注意:您可以使用此方法并将UILabel
替换为您想要子类化的任何其他视图。