分段控件中的渐变色

时间:2018-07-02 17:15:53

标签: ios uikit gradient segmentedcontrol

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

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。任何帮助都非常感谢。

编辑:

理想情况下,我希望分段控件看起来像这样:

enter image description here

2 个答案:

答案 0 :(得分: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
        }
    }

尽管看起来很丑陋,但我已经使用了一段时间了,并且看起来很简单。希望对您有所帮助。

编辑:

这是您需要的伙伴吗?

enter image description here

如果知道是,我将发布相同的代码。

编辑2:

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

免责声明:

正如rmaddy在下面的评论中所提到的,这是一种黑客行为,并利用了未公开的文档(尽管使用了完整的公共API),但是这是一个非常知名的黑客技术,它可以改变UISegemntedControl的色泽,这种颜色一直存在。 iOS 5(这就是我记得的方式,让我知道我是否错了)

因此,请谨慎使用答案,因为在将来的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()
}

希望这会有所帮助

答案 1 :(得分:0)

为 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)
                }
            }
        }
    }
}

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