[iOS] iOS15 UIButton, setTitle with RxSwift
iOS 15에서 버튼이 새롭게 바뀌었는데 예시를 들면서 살펴보겠습니다.
일단 간단하게 버튼을 만들어줍니다.
private let anyButton: UIButton = {
let button = UIButton()
// 1)
var attString = AttributedString("AnyButton")
attString.font = .systemFont(ofSize: 16, weight: .bold)
attString.foregroundColor = .systemPink
// 2)
var configuration = UIButton.Configuration.filled()
configuration.attributedTitle = attString
configuration.image = UIImage(named: "btn_arrowdown_black_10pt")
configuration.imagePadding = 8
configuration.imagePlacement = .trailing
// 3)
configuration.baseBackgroundColor = .white
button.configuration = configuration
return button
}()
1) title에 폰트와 글자색을 추가하기 위해서 AttributedString을 사용합니다.
저는 이 부분이 상당히 귀찮았습니다.
2) .filled()를 사용하면 배경색이 생깁니다.
plain()으로하면 투명한 버튼이 되는데 설명을 위해 .filled()를 사용했습니다.
3) 배경색을 지정합니다. plain()에서는 작동하지 않습니다.
버튼이 잘 보이도록 높이 44의 노란 뷰에 넣고 확인해보겠습니다.
Button에 기본 contentInsets이 있어서 글씨와 그림 주변에 터치가능한 영역이 더 넓어집니다.
이 inset이 있기 때문에 그냥 왼쪽 constraint를 20을 주게되면 실제 원하는 거리보다 더 멀어지게보입니다. (노란뷰가 흰색 바탕일 경우)
inset을 전부 0으로 만들어보겠습니다.
configuration.contentInsets = .init(top: 0, leading: 0, bottom: 0, trailing: 0)
버튼의 여백이 사라지고 처음에 원했던 제약조건대로 버튼이 그려지게 되었습니다.
다음은 RxSwift를 이용한 Binding에서의 문제입니다.
iOS15에서는 setTitle을 사용할 수 없습니다.
정확히 말하면 setTitle은 가능하나 setTitle을 하면 attributeString이 안됩니다.
.drive(anyButton.rx.title())
RxSwift에서 위와 같은 방식으로 많이 사용하는데요.
title()을 보면 아래와 같이 setTitle을 사용합니다.
public func title(for controlState: UIControl.State = []) -> Binder<String?> {
Binder(self.base) { button, title in
button.setTitle(title, for: controlState)
}
}
그래서 다음과 같이 title의 속성들이 전부 사라지게 됩니다.
RxSwift를 사용할 때는 다음과 같이 사용하시면 됩니다.
.drive { [weak self] title in
guard let self = self else { return }
var attString = AttributedString(title)
attString.font = .systemFont(ofSize: 16, weight: .bold)
attString.foregroundColor = .systemPink
self.anyButton.configuration?.attributedTitle = attString
}
추가내용
iOS15이하 지원 시
private let anyButton: UIButton = {
let button = UIButton()
if #available(iOS 15.0, *) {
var attString = AttributedString("AnyButton")
attString.font = .systemFont(ofSize: 16, weight: .bold)
attString.foregroundColor = .systemPink
// 2)
var configuration = UIButton.Configuration.filled()
configuration.attributedTitle = attString
configuration.contentInsets = .init(top: 0, leading: 0, bottom: 0, trailing: 0)
configuration.image = UIImage(named: "btn_arrowdown_black_10pt")
configuration.imagePadding = 8
configuration.imagePlacement = .trailing
// 3)
configuration.baseBackgroundColor = .lightGray
button.configuration = configuration
} else {
button.backgroundColor = .lightGray
button.setTitleColor(.systemPink, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 16, weight: .bold)
button.setImage(UIImage(named: "btn_arrowdown_black_10pt"), for: .normal)
button.semanticContentAttribute = .forceRightToLeft
button.contentEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: 8)
button.imageEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: -16)
}
return button
}()
...
.drive { [weak self] title in
guard let self = self else { return }
if #available(iOS 15.0, *) {
var attString = AttributedString(title)
attString.font = .systemFont(ofSize: 16, weight: .bold)
attString.foregroundColor = .systemPink
self.anyButton.configuration?.attributedTitle = attString
} else {
self.anyButton.setTitle(title, for: .normal)
}
}