【问题标题】:UITextView gradient layer apply not workingUITextView 渐变层应用不起作用
【发布时间】:2020-11-13 11:10:03
【问题描述】:

我想在 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 修改答案后的屏幕截图。

【问题讨论】:

    标签: ios uikit uitextview cagradientlayer


    【解决方案1】:

    @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 替换为您希望子类化的任何其他视图。

    这里是the example project on the GitHub

    【讨论】:

    • 我在 UITextView 中需要它,而不是标签
    • 照注释说的做。只需将UILabel 更改为UITextView。它支持所有UIView 子类。
    • 我已经更新了我的答案以满足您的需要。如您所见,除了UILabelUITextView 之外,不需要进行任何更改。代码中只有一个字;)
    • 我认为图像需要应用于 UITextView 的容器视图,而不是 UITextView。而图像的问题是,如果您需要自定义渐变高度,则需要使用不同的图像。
    • 代码在自动旋转 "EXC_BAD_ACCESS" 行 maskImageView.frame = bounds 时也会崩溃
    【解决方案2】:

    编辑 - 在明确预期效果后...

    关于为什么您只看到顶部支架上的渐变,我最初的回答是:

    您只看到顶部的渐变,因为您为其提供了 四个 位置,但只有 两个颜色。

    所以,既然您提供了您正在尝试做的事情的图片...

    将此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 {
                $0.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 \($0)\n" })
            
        }
    
    }
    

    结果:

    或者,使用蓝色背景而不是图像:


    您只看到顶部的渐变,因为您为其提供了 四个 位置,但只有 两个颜色。

    将颜色更改为:

    gradient.colors = [UIColor.clear.cgColor, UIColor.black.cgColor, UIColor.black.cgColor, UIColor.clear.cgColor]
    

    可能会给你想要的外观......但你需要额外的代码来处理大小变化。

    如果你使用这个类作为你的“容器”视图,大小将是自动的:

    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 {
                $0.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."
    
        }
        
    }
    

    【讨论】:

    • 感谢您的回答,但我认为容器视图不应该是 UITextView。可能是我的问题措辞不正确,但我的意图是让 UITextView 框架的顶部 10% 和底部 10%(而不是边界)具有渐变。我会试试这个,让你知道。
    • 在我的回答中,容器一个UIView子类...
    • 我看到了,但是textView2的作用是什么?它存在只是因为我们事先不知道文本高度,所以它会处理文本高度小于 100 的情况吗?
    • 你检查过代码吗?你试过运行它吗?两个示例:一个具有固定高度的滚动文本视图,另一个具有非滚动自动调整大小的文本视图。
    • 我查看并尝试了您的代码。请参阅问题中的输出图像。不明白 textView2 的用途。
    猜你喜欢
    • 1970-01-01
    • 2014-03-15
    • 1970-01-01
    • 2016-12-23
    • 2017-11-12
    • 1970-01-01
    • 1970-01-01
    • 2018-12-12
    • 1970-01-01
    相关资源
    最近更新 更多