Hello, I was also struggling a bit with tvos default focus color. The only solution I came up with was to use basic UIButton() initializer. If you use, for example, UIButton(type: .plain), white color will always be on top of your background color. The problem with this approach is that you lose the default parallax effect. I worked around this issue by implementing a custom logic for increasing button size when focused. It's a bit hacky solution, but I hope it helps you.
Here is an example how you can code it:
import UIKit
struct ColorConfiguration {
var defaultBackgroundColor: UIColor
var defaultTintColor: UIColor
var focusedBackgroundColor: UIColor
var focusedTintColor: UIColor
static let exampleColors1 = ColorConfiguration(
defaultBackgroundColor: .blue,
defaultTintColor: .white,
focusedBackgroundColor: .yellow,
focusedTintColor: .black)
static let exampleColors2 = ColorConfiguration(
defaultBackgroundColor: .red,
defaultTintColor: .gray,
focusedBackgroundColor: .green,
focusedTintColor: .white)
}
class CustomFocusColorButton: UIButton {
private let focusedScale: CGFloat = 1.05
private let colorConfig: ColorConfiguration
init(colorConfig: ColorConfiguration) {
self.colorConfig = colorConfig
super.init(frame: .zero)
layer.cornerRadius = 10
backgroundColor = colorConfig.defaultBackgroundColor
setTitleColor(colorConfig.defaultTintColor, for: .normal)
tintColor = colorConfig.defaultTintColor
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
super.pressesBegan(presses, with: event)
guard presses.first?.type == .select else { return }
UIView.animate(withDuration: 0.1) {
self.transform = .identity
}
}
override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
super.pressesEnded(presses, with: event)
UIView.animate(withDuration: 0.1) {
if self.isFocused {
self.transform = CGAffineTransform(scaleX: self.focusedScale, y: self.focusedScale)
} else {
self.transform = .identity
}
}
}
override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
super.didUpdateFocus(in: context, with: coordinator)
if context.nextFocusedView == self {
coordinator.addCoordinatedAnimations {
self.backgroundColor = self.colorConfig.focusedBackgroundColor
self.setTitleColor(self.colorConfig.focusedTintColor, for: .normal)
self.tintColor = self.colorConfig.focusedTintColor
self.transform = CGAffineTransform(scaleX: self.focusedScale, y: self.focusedScale)
}
} else {
coordinator.addCoordinatedAnimations {
self.backgroundColor = self.colorConfig.defaultBackgroundColor
self.setTitleColor(self.colorConfig.defaultTintColor, for: .normal)
self.tintColor = self.colorConfig.defaultTintColor
self.transform = .identity
}
}
}
}
import UIKit
class TestViewController: UIViewController {
let button1 = CustomFocusColorButton(colorConfig: .exampleColors1)
let button2 = CustomFocusColorButton(colorConfig: .exampleColors2)
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(button1)
button1.translatesAutoresizingMaskIntoConstraints = false
button1.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button1.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -100).isActive = true
button1.widthAnchor.constraint(equalToConstant: 200).isActive = true
button1.heightAnchor.constraint(equalToConstant: 80).isActive = true
view.addSubview(button2)
button2.translatesAutoresizingMaskIntoConstraints = false
button2.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button2.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
button2.widthAnchor.constraint(equalToConstant: 200).isActive = true
button2.heightAnchor.constraint(equalToConstant: 80).isActive = true
button1.setTitle("Button 1", for: .normal)
button2.setTitle("Button 2", for: .normal)
}
}