如何保持 UIButton 的图像颜色不会重置为默认值?

Posted

技术标签:

【中文标题】如何保持 UIButton 的图像颜色不会重置为默认值?【英文标题】:How can I keep the image color of UIButton from resetting to default? 【发布时间】:2018-05-29 19:49:56 【问题描述】:

这个问题是关于 Swift 4,Xcode 9.3.1。

更改图像按钮颜色的过程有据可查。这不是问题。

这个问题是关于如果按钮恰好是IBAction 内的目标,为什么图像按钮的颜色会重置为默认颜色,以及如何处理它。

我在 Xcode 中创建了一个新项目来演示我所看到的。这是一个在视图控制器中有一个 IBAction 的项目,它试图更改按钮上图像的颜色。我在应用程序中添加了三个按钮(oneButtontwoButtonthreeButton),并将它们连接起来,因此每个按钮都有以下插座:

每个按钮都定义了不同的图像。这是一个例子:

这是ViewController

import UIKit

class ViewController: UIViewController 

  @IBOutlet weak var oneButton: UIButton!
  @IBOutlet weak var twoButton: UIButton!
  @IBOutlet weak var threeButton: UIButton!

  override func viewDidLoad() 
    super.viewDidLoad()
  
  override func didReceiveMemoryWarning() 
    super.didReceiveMemoryWarning()
  

  @IBAction func myAction(sender: UIButton) 
    let oneImageView = oneButton.imageView
    if sender == oneButton 
      print("oneButton")
      oneImageView?.setImageColor(color: UIColor.red)
     else if sender == twoButton 
      print("twoButton")
      oneImageView?.setImageColor(color: UIColor.blue)
     else if sender == threeButton 
      print("threeButton")
      oneImageView?.setImageColor(color: UIColor.purple)
    
  


extension UIImageView 
  func setImageColor(color: UIColor) 
    let templateImage = self.image?.withRenderingMode(UIImageRenderingMode.alwaysTemplate)
    self.image = templateImage
    self.tintColor = color
  

单击每个图标会在输出窗口中显示相应的文本(oneButtontwoButtonthreeButton),具体取决于单击的是哪个图标。效果很好。

应用启动时,第一张图片的颜色是黑色(默认),正如预期的那样。

单击twoButton 会使第一张图像上的颜色变为蓝色,正如预期的那样。

单击threeButton 会使第一张图像上的颜色变为紫色,正如预期的那样。

单击oneButton 会使第一张图像上的颜色重置为默认值(黑色)。这是预期的。

我想正在发生的事情是,因为按钮当前正在处理系统事件(触摸),所以我设置的颜色被某些系统进程清除了。

我更改了代码,而不是Touch Up Inside,而是调用myAction() Touch Down,并且第一个图像的颜色变为红色!但只是在触摸的时候……我一松开,颜色就恢复了默认。

我想要的是能够触摸 oneButton 并将其更改为代码中的任何颜色,并保持这种状态,而不是将其重置为默认颜色。

添加/编辑

上面的代码确实反复设置了图像渲染模式。我意识到,不应该有必要这样做。但是当我不这样做时,应用程序在模拟器中运行时不会更改颜色。只要不是被触摸的项目,上面的代码至少会改变图像的颜色。在下面的代码中,虽然myAction() 中的if/else 的相应部分运行,但设置.tintColor 完全没有效果,无论单击哪个项目。我尝试了experiment真假,无济于事。

class ViewController: UIViewController 

  @IBOutlet weak var oneButton: UIButton!
  @IBOutlet weak var twoButton: UIButton!
  @IBOutlet weak var threeButton: UIButton!

  var oneImageView: UIImageView!
  var oneImage: UIImage!

  override func viewDidLoad() 
    super.viewDidLoad()
    oneImageView = oneButton.imageView
    oneImage = oneImageView?.image
    let experiment = true
    if experiment 
      oneImageView?.image = UIImage(named: "image")?.withRenderingMode(.alwaysTemplate)
     else 
      oneImage.withRenderingMode(.alwaysTemplate)
      oneImageView.image = oneImage
    
  
  override func didReceiveMemoryWarning() 
    super.didReceiveMemoryWarning()
  

  @IBAction func myAction(sender: UIButton) 
    //let oneImageView = oneButton.imageView
    let tintColor: UIColor = 
      print("switch doesn't work here for some reason")
      if sender == oneButton 
        print("oneButton")
        return .red
       else if sender == twoButton 
        print("twoButton")
        return .blue
       else if sender == threeButton 
        print("threeButton")
        return .purple
       else 
        return .cyan
      
    ()
    oneImageView?.tintColor = tintColor
  

【问题讨论】:

这可能不是您正在寻找的,但是...您是否尝试过 (1) 将按钮类型从 custom 更改为system 然后 (2) 设置 tintColor?这就是我设置图像颜色的方式。 我确实尝试过更改按钮类型。这并没有帮助,但解决问题的方法是 xcassets 并将Render As 更改为Template Image(来自Default)。出于某种原因,将代码中的渲染模式设置为 .alwaysTemplate 并没有提供所需的结果。 你不应该直接改变按钮图像。有一些特殊的方法。请注意,按钮的每个状态都有一个图像。 @Sulthan,实际应用中按钮的状态不仅取决于按钮的状态,还取决于其他事物的状态,因此需要手动管理颜色。 @Dale 我只是说你不应该手动更改imageView.image,因为每次更改按钮状态时都会设置它(例如,突出显示、选择等时)。你应该改用setImage(UIImage?, for: UIControlState) 【参考方案1】:

设置模板图片

简短的回答是,您希望应用颜色的图像需要指定为模板图像。如果在图像资产的属性检查器中进行了此指定,则原始问题中的所有代码都将起作用。对.withRenderingMode(.alwaysTemplate) 的任何调用都将变得不必要。

在属性检查器中

除非图像设置为模板图像,否则不会应用颜色。

当前的工作方式(Xcode 9.4、Swift 4.1)是在 GUI 属性检查器中将图像设置为模板图像。

完成此操作后,原始问题和已编辑问题中的所有代码版本都应按预期工作。

在代码中

在代码中将图像设置为模板图像似乎应该工作,但是,至少对于模拟器上的 Xcode 9.4、Swift 4.1,它没有永久性;第一次用户触摸会重置它。

在下面的代码中,.withRenderingMode(.alwaysTemplate) 确实会导致图标成为模板,但是一旦用户第一次触摸图标,它就会再次成为默认渲染。 这是真的在模拟 iPhone 中(未在物理设备上测试)。

override func viewDidLoad() 
  super.viewDidLoad()
  oneImageView = oneButton.imageView
  // NOT PERMANENT
  oneImageView?.image = UIImage(named:   "image")?.withRenderingMode(.alwaysTemplate) 

【讨论】:

【参考方案2】:

您不需要一次又一次地设置图像,只需将图像设置一次作为模板并稍后更改色调颜色。此外,使用switch 而不是多个ifs 更简洁。

override func viewDidLoad() 
    super.viewDidLoad()

    oneImageView?.image = UIImage(named: "image").withRenderingMode(.alwaysTemplate)


@IBAction func myAction(_ sender: UIButton) 
    let tintColor: UIColor = 
        switch sender 
            case oneButton: return .red
            case twoButton: return .blue
            case threeButton: return .purple
            default: return .clear
        
    ()

    oneImageView?.tintColor = tintColor

【讨论】:

问题中的代码并不是为了优雅。这是为了证明一个问题。我认为您的回答没有解决将颜色重置为默认值的问题。 问题是你不必要地扩展 UIImageView 和设置图像。这种访问和修改图像的方式会导致您描述的重置。 感谢您的想法。我试图实现它,但无法让它工作(我将代码添加到原始问题中)。我尝试了几种变体,但设置 tintColor 从来没有做任何事情(myAction 运行,返回了适当的 tintColor,但设置 tintColor 没有效果)。我可以看到这应该可以工作,但它就是行不通,至少在我发布的这个微小的示例应用程序中是这样。 我在一个示例应用程序中实现了代码,它运行良好。你确定oneImageView 在执行myAction 时不为零吗?另外,oneImageView 的图像是否呈现为模板? 您的实现是使用带有图像的UIButtons,它有效吗?哇。我的“从头开始”应用程序具有所有默认值......点击次数很少......不确定我错过了哪个点击。当myAction 被执行时,oneImageView 肯定不是零。在按钮的属性检查器中,我找不到按钮中包含的图像的“渲染为”以将其设置为模板。这就是我在代码中这样做的原因。

以上是关于如何保持 UIButton 的图像颜色不会重置为默认值?的主要内容,如果未能解决你的问题,请参考以下文章

iOS:如果背景图像与按钮颜色相同,则更改我的 UIButton 的颜色

如何在保持图像纵横比的同时调整 UIButton 中的图像大小?

如何更改图像 UIButton 的大小和颜色?

缩放时保持 UIButton 相对

可以重置 UIButton 背景图像吗?

调用 setImage: 后 UIButton 不会更新它的图像