分段控制中的渐变色调颜色

Posted

技术标签:

【中文标题】分段控制中的渐变色调颜色【英文标题】:Gradient tint color in segmented control 【发布时间】:2018-07-02 17:15:52 【问题描述】:

我用这种方法得到渐变图像

func gradient(size:CGSize,color:[UIColor]) -> UIImage?
    //turn color into cgcolor
    let colors = color.map$0.cgColor
    //begin graphics context
    UIGraphicsBeginImageContextWithOptions(size, true, 0.0)
    guard let context = UIGraphicsGetCurrentContext() else 
        return nil
    
    // From now on, the context gets ended if any return happens
    defer UIGraphicsEndImageContext()
    //create core graphics context
    let locations:[CGFloat] = [0.0,1.0]
    guard let gredient = CGGradient.init(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colors as NSArray as CFArray, locations: locations) else 
        return nil
    
    //draw the gradient
    context.drawLinearGradient(gredient, start: CGPoint(x:0.0,y:size.height), end: CGPoint(x:size.width,y:size.height), options: [])
    // Generate the image (the defer takes care of closing the context)
    return UIGraphicsGetImageFromCurrentImageContext()

然后我将分段控件的tintColor设置为渐变:

    let gradientImage = gradient(size: listSegmentedControl.frame.size, color: [UIColor.black, UIColor.red])!
    listSegmentedControl.tintColor = UIColor(patternImage: gradientImage)

这是行不通的。但是,同样的代码也适用于设置 backgroundColor:

    let gradientImage = gradient(size: listSegmentedControl.frame.size, color: [UIColor.black, UIColor.red])!
    listSegmentedControl.backgroundColor = UIColor(patternImage: gradientImage)

有人知道为什么吗?我真的需要设置渐变 tintColor。非常感谢任何帮助。

编辑:

理想情况下,我希望我的分段控件如下所示:

【问题讨论】:

这可能会有所帮助:***.com/questions/34733943/… @Dopapp 谢谢!我试试看 【参考方案1】:

这是一个已知的更改UISegmentedControl 色调颜色的技巧

   let sortedViews = listSegmentedControl.subviews.sorted( by:  $0.frame.origin.x < $1.frame.origin.x  )

    for (index, view) in sortedViews.enumerated() 
        if index == listSegmentedControl.selectedSegmentIndex 
            view.tintColor = UIColor(patternImage: gradientImage)
         else 
            view.tintColor = UIColor.gray //Whatever the color of non selected segment controller tab
        
    

虽然看起来像一个丑陋的黑客,但我已经使用了很长一段时间,而且看起来相当简单。希望对您有所帮助。

编辑:

这是你需要的吗?

如果是,请让我知道,我会发布相同的代码。

编辑 2:

正如 OP 在他的评论中提到的那样,他期望的输出与我在上图中显示的输出相同,并提供相同的代码。

免责声明:

正如rmaddy 在下面的 cmets 中所提到的,这是一个 hack,它使用了未记录的(尽管是完整的公共 API),但这是一个众所周知的 hack,用于更改从 ios 5 开始就存在的 UISegemntedControl 的色调颜色(这就是我记得的方式,让我知道我是否错了)

因此请谨慎使用 answer ,在未来的 iOS 版本中,Apple 可能会更改 UISegemntedControl 中子视图的结构,并可能影响您的 O/P。我看不到的任何东西都不会导致崩溃,但可能会影响 O/P 在屏幕上的呈现方式。

我已经声明了一个变量,以便 GradientImage 只能生成一次,但它取决于你的实现以你想要的方式使用它

var gradientImage : UIImage! = nil

ViewDidLoad 中,我将gradientImageUISegmentedControl 初始化为

override func viewDidLoad() 
        super.viewDidLoad()
        gradientImage = gradient(size: segmentControl.frame.size, color: [UIColor.black, UIColor.red])!

        //I have specified custom font need not necessarily be used
        //Font color attribute is important though, usually `UISegementedControl` title takes color from tint color, because we might need a different color for text to highlight above gradient color am using custom font colors

        let font = UIFont(name: "HelveticaNeue-Medium", size: 20)
        segmentControl.setTitleTextAttributes([NSFontAttributeName : font!, NSForegroundColorAttributeName : UIColor.blue], for: .normal)
        segmentControl.setTitleTextAttributes([NSForegroundColorAttributeName : UIColor.white], for: .selected)

        //Set the border color and border to `UISegmentedControl` and also make it round corner

        segmentControl.layer.borderColor = UIColor(patternImage: gradientImage).cgColor
        segmentControl.layer.borderWidth = 2
        segmentControl.layer.masksToBounds = true
        segmentControl.layer.cornerRadius = 10

        //In order to update the selected Segment tint and background color we need to call multiple statements every time selection changes hence I have moved it to the function and called it in viewDidLoad

        updateGradientBackground()
    

最后 updateGradientBackground 函数定义与我在原始答案中发布的相同

fileprivate func updateGradientBackground() 
        let sortedViews = segmentControl.subviews.sorted( by:  $0.frame.origin.x < $1.frame.origin.x  )
        for (index, view) in sortedViews.enumerated() 
            if index == segmentControl.selectedSegmentIndex 
                //very important thing to notice here is because tint color was not honoring the `UIColor(patternImage` I rather used `backgroundColor` to create the effect and set clear color as clear color
                view.backgroundColor = UIColor(patternImage: self.gradientImage)
                view.tintColor = UIColor.clear
             else 
                //very important thing to notice here is because tint color was not honoring the `UIColor(patternImage` I rather used `backgroundColor` to create the effect and set clear color as clear color
                view.backgroundColor = UIColor.white //Whatever the color of non selected segment controller tab
                view.tintColor = UIColor.clear
            
        
    

最后,在UISegmentedControl的IBAction中,简单调用

@IBAction func segmentControllerTapped(_ sender: UISegmentedControl) 
    self.updateGradientBackground()

希望对你有帮助

【讨论】:

请注意,此代码依赖于UISegmentedControl 的未记录和私有子视图结构。此代码可能会因任何更改该私有子视图结构的 iOS 更新而失败。至少不应该崩溃。 @rmaddy:同意。因此我提到它是一个丑陋的黑客。一个目前有效但运行时有可能在未来被破坏的黑客,据说这个黑客似乎从 iOS5-6 到现在都运行良好。没有提及任何为我的回答辩护:) 感谢您的回答!我在我的应用程序中实现了您的代码,但它不起作用。我一定是做错了什么。你能从你身边检查一下,让我知道它是否适合你吗? @lavrin-pristazh:现在检查 我只是稍微更改了您的代码,并完全按照我的意愿设置了分段控件。非常感谢,你拯救了我的一天:)【参考方案2】:

iOS13+ UISegmentControl 上的旋钮实现渐变色。添加带有渐变的子视图,然后为其设置遮罩并复制动画以匹配原始旋钮。

// 1 - Make subclass
class GradientSegment: UISegmentedControl 
    // this could be UIImageView
    lazy var gradientView: GradientView = 
        let view = GradientView(frame: self.bounds)
        view.startColor = ...
        view.endColor = ...
        view.horizontalMode = true
        view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
        return view
    ()

    let gradientMask = UIImageView()

    // 2 - override init
    override init(frame: CGRect) 
        super.init(frame: frame)
        setup()
    
    
    required init?(coder: NSCoder) 
        super.init(coder: coder)
        setup()
    

    private func setup() 
        addSubview(gradientView)
        gradientView.mask = gradientMask
    

    // 3 - Copy knob position and animations
    override func insertSubview(_ view: UIView, at index: Int) 
        super.insertSubview(view, at: index)
        if index == 3, let view = view as? UIImageView 
            gradientMask.image = view.image
            gradientMask.frame = view.frame

            if let keys = view.layer.animationKeys() 
                for key in keys 
                    guard let animation = view.layer.animation(forKey: key) else 
                        continue
                    
                    gradientMask.layer.add(animation, forKey: key)
                
            
        
    

这是不安全的做法,因为当苹果改变控件布局时外观会变得无效,但现在它正在工作。

【讨论】:

以上是关于分段控制中的渐变色调颜色的主要内容,如果未能解决你的问题,请参考以下文章

LabVIEW如何设计渐变色的进度条

div背景颜色怎样渐变 css实现div层背景颜色渐变代码

如何用css使边框颜色渐变

DW里单元格怎么设置渐变色啊?

CSS背景渐变

css 如何实现 颜色的渐变??