You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何实现滚动视图内随背景动态变色的文本?

实现滚动视图中随背景动态变色的文本(Swift)

一、基础布局搭建

先完成滚动视图、背景图和文本标签的基础配置,确保滚动功能正常:

import UIKit

class ViewController: UIViewController, UIScrollViewDelegate {
    
    // 滚动视图
    private let scrollView = UIScrollView()
    // 背景图片容器
    private let backgroundImageView = UIImageView()
    // 文本标签(用富文本控制单个字符颜色)
    private let textLabel = UILabel()
    
    // 替换成你的测试文本和背景图
    private let sampleText = "滚动查看字符颜色随背景切换的效果,黑白背景对应反向文本色"
    private let backgroundImage = UIImage(named: "黑白背景图")!

    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        scrollView.delegate = self
        // 初始化文本颜色
        updateTextColors()
    }
    
    private func setupUI() {
        // 配置滚动视图
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(scrollView)
        
        // 配置背景图
        backgroundImageView.image = backgroundImage
        backgroundImageView.contentMode = .scaleToFill
        backgroundImageView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(backgroundImageView)
        
        // 配置文本标签
        textLabel.font = UIFont.systemFont(ofSize: 40, weight: .bold)
        textLabel.numberOfLines = 0
        textLabel.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(textLabel)
        
        // 设置约束
        NSLayoutConstraint.activate([
            scrollView.topAnchor.constraint(equalTo: view.topAnchor),
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            
            backgroundImageView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            backgroundImageView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            backgroundImageView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
            backgroundImageView.heightAnchor.constraint(equalToConstant: 2000), // 按需调整背景图高度
            backgroundImageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            
            textLabel.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 100),
            textLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 20),
            textLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -20),
            textLabel.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -100)
        ])
        
        // 设置滚动视图内容尺寸
        scrollView.contentSize = CGSize(width: view.bounds.width, height: 2000)
    }
}

二、核心方法:获取背景指定位置的颜色

实现一个工具方法,根据屏幕坐标从背景图中提取对应像素的颜色,判断是黑还是白:

extension ViewController {
    // 获取背景图上指定屏幕点的颜色
    private func getBackgroundColor(at point: CGPoint) -> UIColor {
        // 把屏幕坐标转换为背景图的像素坐标(考虑缩放和滚动偏移)
        let imagePoint = CGPoint(
            x: point.x / backgroundImageView.bounds.width * backgroundImage.size.width,
            y: (point.y + scrollView.contentOffset.y) / backgroundImageView.bounds.height * backgroundImage.size.height
        )
        
        // 确保点在图片范围内
        guard imagePoint.x >= 0, imagePoint.x < backgroundImage.size.width,
              imagePoint.y >= 0, imagePoint.y < backgroundImage.size.height else {
            return .white // 超出范围默认白色
        }
        
        // 读取图片像素数据
        guard let cgImage = backgroundImage.cgImage,
              let dataProvider = cgImage.dataProvider,
              let data = dataProvider.data else {
            return .white
        }
        
        let bytes = CFDataGetBytePtr(data)
        let bytesPerPixel = cgImage.bitsPerPixel / 8
        let bytesPerRow = cgImage.bytesPerRow
        let pixelOffset = Int(imagePoint.y) * bytesPerRow + Int(imagePoint.x) * bytesPerPixel
        
        // 计算灰度值(RGBA格式)
        let red = CGFloat(bytes![pixelOffset]) / 255.0
        let green = CGFloat(bytes![pixelOffset + 1]) / 255.0
        let blue = CGFloat(bytes![pixelOffset + 2]) / 255.0
        let gray = (red + green + blue) / 3.0
        
        // 灰度>0.5视为白色,文本用黑色;反之文本用白色
        return gray > 0.5 ? .black : .white
    }
}

三、实时更新文本颜色

监听滚动视图的滚动事件,遍历每个字符的位置,获取对应背景色后更新文本颜色:

extension ViewController {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        updateTextColors()
    }
    
    private func updateTextColors() {
        let attributedString = NSMutableAttributedString(string: sampleText)
        
        // 遍历每个字符
        for index in 0..<sampleText.count {
            let charRange = NSRange(location: index, length: 1)
            // 获取字符在标签内的矩形区域
            let charRect = textLabel.boundingRect(forCharacterRange: charRange, in: textLabel.bounds)
            
            // 取字符区域中心点作为采样点
            let centerPoint = CGPoint(
                x: charRect.midX + textLabel.frame.origin.x,
                y: charRect.midY + textLabel.frame.origin.y - scrollView.contentOffset.y
            )
            
            // 设置字符颜色
            let textColor = getBackgroundColor(at: centerPoint)
            attributedString.addAttribute(.foregroundColor, value: textColor, range: charRange)
        }
        
        textLabel.attributedText = attributedString
    }
}

// 给UILabel添加获取单个字符区域的扩展方法
extension UILabel {
    func boundingRect(forCharacterRange range: NSRange, in bounds: CGRect) -> CGRect {
        let layoutManager = NSLayoutManager()
        let textContainer = NSTextContainer(size: bounds.size)
        let textStorage = NSTextStorage(attributedString: self.attributedText ?? NSAttributedString(string: self.text ?? ""))
        
        layoutManager.addTextContainer(textContainer)
        textStorage.addLayoutManager(layoutManager)
        
        textContainer.lineFragmentPadding = 0.0
        textContainer.maximumNumberOfLines = self.numberOfLines
        textContainer.lineBreakMode = self.lineBreakMode
        
        let glyphRange = layoutManager.glyphRange(forCharacterRange: range, actualCharacterRange: nil)
        return layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
    }
}

四、新手优化提示

  • 替换backgroundImage为你自己的黑白背景图,确保图片已导入项目。
  • 背景图高度和滚动视图contentSize要匹配,避免滚动异常。
  • 灰度判断阈值0.5可根据你的背景图微调,让颜色切换更准确。
  • 如果文本很长,可优化为只更新当前屏幕可见的字符,减少性能消耗。

内容的提问来源于stack exchange,提问作者Богдан Бабенко

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 08:55:04