iOS

[iOS] iOS15 UIButton, setTitle with RxSwift

Kim_Baechu 2022. 2. 11. 16:53

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