如何在Swift中设置UIButton在UIControlState.Highlighted状态下的背景颜色?

55
我可以为按钮设置背景颜色,但我不知道如何设置高亮状态下的背景颜色UIControlState.Highlighted。这是否可能?还是我需要采用setBackgroundImage的方式?

请查看此帖子 https://somethingaboutios.wordpress.com/2016/02/09/uibutton-backgroundcolor-for-uicontrolstateselected/,其中包含Swift示例。 - Gabriel.Massana
12个回答

154
如果有人路过,另一种更容易的方法可能更适合你,如果你需要多次使用......我为UIButton编写了一个简短的扩展,它完全正常工作:

适用于Swift 3

extension UIButton {
    func setBackgroundColor(color: UIColor, forState: UIControlState) {
        UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
        CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), color.CGColor)
        CGContextFillRect(UIGraphicsGetCurrentContext(), CGRect(x: 0, y: 0, width: 1, height: 1))
        let colorImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        self.setBackgroundImage(colorImage, forState: forState)
    }
}

适用于Swift 4

extension UIButton {
    func setBackgroundColor(color: UIColor, forState: UIControl.State) {
        self.clipsToBounds = true  // add this to maintain corner radius
        UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
        if let context = UIGraphicsGetCurrentContext() {
            context.setFillColor(color.cgColor)
            context.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
            let colorImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            self.setBackgroundImage(colorImage, for: forState)
        }
    }
}

你使用它的方式与setBackgroundImage相同:

yourButton.setBackgroundColor(color: UIColor.white, forState: UIControl.State.highlighted)

18
这将破坏自动布局并移除圆角半径。 - user4788711
1
@user4788711 就像设置背景图片一样。不多也不少。 - winterized
13
要修复按钮的圆角,您只需在按钮上启用“剪辑子视图”选项。 - heximal
2
对我来说,为UIImage添加一个颜色初始化器是完全值得的。这样,该函数的主体就变成了self.setBackgroundImage(UIImage(color: color), forState: forState) - Phlippie Bosman
1
为什么需要使用图形上下文?button.setBackgroundImage(UIImage(color: .red), for: .normal)可以在一行中工作。 - Marco Pappalardo
显示剩余5条评论

32

@winterized extension 的语法更改为 Swift 3+ 语法

extension UIButton {
    func setBackgroundColor(color: UIColor, forState: UIControlState) {
        UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
        UIGraphicsGetCurrentContext()!.setFillColor(color.cgColor)
        UIGraphicsGetCurrentContext()!.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
        let colorImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.setBackgroundImage(colorImage, for: forState)
    }}

17
以下是一种方法。有两个IBActions。一个用于在按下按钮时控制背景颜色,另一个是释放时。
注:IBAction是指Interface Builder Action(接口生成器操作),是iOS应用程序开发中用于响应用户交互的动作函数的一种特殊类型。
import UIKit

class ViewController: UIViewController {


    @IBOutlet weak var button: UIButton!

    @IBAction func buttonClicked(sender: AnyObject) { //Touch Up Inside action
        button.backgroundColor = UIColor.whiteColor()
    }

    @IBAction func buttonReleased(sender: AnyObject) { //Touch Down action
        button.backgroundColor = UIColor.blueColor()

    }

    override func viewDidLoad() {
        super.viewDidLoad()

    }
}

在添加句点后查看按钮的自动完成选项时,您可以设置背景颜色,但不能设置指定状态的背景颜色。 您只能设置背景图片。 当然,如果您坚持使用这种方法而不是使用我上面展示的方法,您可以使用setbackgroundImageForState属性加载所需颜色的图像作为背景图像。

输入图像描述

输入图像描述


2
该解决方案仅适用于ViewController,而不适用于TableViewController。按下操作在TVC中也可以工作,但只有几毫秒的延迟,因此快速触摸屏幕不会改变按钮的颜色。我认为按钮所在的单元格存在问题,但我不确定。 - S. Birklin
我遇到了完全相同的问题。如果我只是轻触视图,那么什么也不会发生,因为颜色已经恢复到旧颜色。我可以通过稍微延迟一下来解决这个问题,这样颜色就会在几毫秒后切换回来。这样即使用户只轻按一下,颜色也是可见的。 - Philipp Otto
1
此外,我认为“Touch Down”方法很冒险。首先,它不会自动重置颜色。这很容易,在“Touch-up Inside”中将其设置回原始颜色即可。但是如果用户做出异常操作,比如按下然后决定不继续,并拖动手指离开。颜色不会重置。 - Dave G

9

针对接受的答案的Swift 4+兼容性:

extension UIButton {

  /// Sets the background color to use for the specified button state.
  func setBackgroundColor(color: UIColor, forState: UIControlState) {

    let minimumSize: CGSize = CGSize(width: 1.0, height: 1.0)

    UIGraphicsBeginImageContext(minimumSize)

    if let context = UIGraphicsGetCurrentContext() {
      context.setFillColor(color.cgColor)
      context.fill(CGRect(origin: .zero, size: minimumSize))
    }

    let colorImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    self.clipsToBounds = true
    self.setBackgroundImage(colorImage, for: forState)
  }
}

兼容SwiftLint并修复自动布局/圆角半径错误的漏洞。


1
你可以将按钮类型从“自定义”更改为“系统”。 - Maximelc

5

这是 Swift 4 版本的此解决方案

extension UIButton {

    func setBackgroundColor(_ color: UIColor, for state: UIControlState) {

        UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
        UIGraphicsGetCurrentContext()!.setFillColor(color.cgColor)
        UIGraphicsGetCurrentContext()!.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
        let colorImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        setBackgroundImage(colorImage, for: state)
    }
}

4
对代码进行微小的改进,避免使用强制解包,请尝试这样做:if let currentGraphicsContext = UIGraphicsGetCurrentContext() { currentGraphicsContext.setFillColor(color.cgColor) currentGraphicsContext.fill(CGRect(x: 0, y: 0, width: 1, height: 1)) }此外,添加layer.masksToBounds = true以保持layer.cornerRadius的功能。 - T. Hyldgaard

4
似乎这里还没有人提到使用“键值观察”(Key Value Observation),但这是另一种方法。与其他答案不同的原因是,您无需一直创建新图像,也不必担心将图像分配给按钮的副作用(例如cornerRadius效果)。
但是,您需要为观察者创建一个类,该观察者将负责存储不同的背景颜色并在observeValue()方法中应用它们。
public class ButtonHighlighterObserver: NSObject {

  var observedButton:UIButton? = nil

  var backgroundColor: UIColor            = UIColor.white
  var backgroundHighlightColor: UIColor   = UIColor.gray

  public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

    // Perform background color changes when highlight state change is observed
    if keyPath == "highlighted", object as? UIButton === observedButton {
        observedButton!.backgroundColor = observedButton!.isHighlighted ? self.backgroundHighlightColor : self.backgroundColor
    }
}

然后你只需要在操作期间管理addObserver / removeObserver:

// Add observer to button's highlighted value
button.addObserver(anObserver, forKeyPath: "highlighted", options: [.new], context: nil)
anObserver.observedButton = button

// ...

// And at deinit time, be sure you remove the observer again
anObserver.observedButton?.removeObserver(item, forKeyPath: "highlighted")
anObserver.observedButton = nil

4

更新Swift 4

 extension UIButton {

    func setBackgroundColor(color: UIColor, forState: UIControlState) {

        UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
        UIGraphicsGetCurrentContext()!.setFillColor(color.cgColor)
        UIGraphicsGetCurrentContext()!.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
        let colorImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()


        self.setBackgroundImage(colorImage, for: forState)
    }

事件按钮操作

显示触摸时高亮


2

为什么不呢?

@implementation UIButton (Color)

- (void) setBackgroundColor:(UIColor*)color forState:(UIControlState)state
{
    [self setBackgroundImage:[UIImage.alloc initWithCIImage:[CIImage imageWithColor:[CIColor colorWithCGColor:color.CGColor]]] forState:state];
}

@end

1
我尝试了这个,但是我不得不裁剪图片,例如将宽度裁剪为1、高度裁剪为1才能使其正常工作。由于imageWithColor导致的无限范围,它无法工作。 - Felix Lieb

2

isHighlighted被设置时,您可以重写isHighlighted并更改背景颜色。

示例:TextButton.Swift

import UIKit

class TextButton: UIButton {

   private var text: String = "Submit" {
       didSet{
           setText()
       }
   }

   var hightlightedColor : UIColor = UIColor(red: 50/255, green: 50/255, blue: 50/255, alpha: 1)

   var background :UIColor = .black {
       didSet{
           self.backgroundColor = background
       }
   }

   override var isHighlighted: Bool {
       didSet {
           self.backgroundColor = self.isHighlighted ? hightlightedColor : background
       }
   }

   // MARK: - Lifecycle
   override init(frame: CGRect) {
       super.init(frame: frame)
       sharedLayout()
   }

   required init?(coder aDecoder: NSCoder) {
       super.init(coder: aDecoder)
       sharedLayout()
   }



   // MARK: - Method
   private func setText() {
       self.setTitle(text, for: .normal)
   }

   private func sharedLayout() {
       self.setTitle(text, for: .normal)
       self.backgroundColor = self.isHighlighted ? .green : background
       self.layer.cornerRadius = 8
   }

用途:

let nextBtn = TextButton()

1

Swift 5

在我的情况下,我需要其他的东西,因为使用扩展和 setBackgroundColor 函数的选项在我们有不同的背景按钮颜色用于暗/亮模式,并且当我们更改 traitCollection.userInterfaceStyle(暗/亮模式)时无法正常工作。所以我使用了自定义实现的 UIButton 类并覆盖了属性 isHighlightedisEnabled

import UIKit

class CustomButton: UIButton {

override init(frame: CGRect) {
    super.init(frame: frame)
    self.isEnabled = isEnabled
 }

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
 }

override var isHighlighted: Bool {
    didSet {
        self.backgroundColor = isHighlighted ? UIColor.blue : UIColor.cyan
    }
 }

override var isEnabled: Bool {
    didSet {
        self.backgroundColor = isEnabled ? UIColor.cyan : UIColor.red
    }
 }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接