버튼을 장착한 Toolbar를 textField.inputAccessoryView에 넣어준다.
textView, textField에서 사용하기 때문에, Button을 들고 있는 커스텀 Toolbar객체를 정의했다.
import Combine
import UIKit
class BaseKeyboardToolbar: UIToolbar {
// MARK: - Properties
var tapPublisher: AnyPublisher<Void, Never> { self.tapSubject.eraseToAnyPublisher() }
private let tapSubject: PassthroughSubject<Void, Never> = .init()
private let doneButton: UIButton = {
let btn = UIButton()
btn.setTitle("완료", for: .normal)
btn.setTitleColor(UIColor(fnColor: .gray3), for: .normal)
return btn
}()
private var subscriptions = Set<AnyCancellable>()
// MARK: - LifeCycle
init() {
super.init(frame: CGRect(
x: .zero,
y: .zero,
width: UIScreen.main.bounds.width,
height: 50
))
self.setupToolbar()
self.bind()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Private Helpers
private func bind() {
self.doneButton.publisher(for: .touchUpInside)
.receive(on: RunLoop.main)
.sink { [weak self] _ in
self?.tapSubject.send()
}
.store(in: &self.subscriptions)
}
private func setupToolbar() {
let emptySpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let barButtonItem = UIBarButtonItem(customView: self.doneButton)
self.items = [emptySpace, barButtonItem]
}
}
textField와 textView는 패딩을 위해 커스텀 객체로 사용하고 있어서, toolbar를 프로퍼티로 갖고 inputAccessoryView에 주입해주었다.
// in 커스텀 TextView
private func setupToolbar() {
self.inputAccessoryView = self.keyboardToolbar
}
private func bind() {
self.keyboardToolbar.tapPublisher
.receive(on: RunLoop.main)
.sink { [weak self] _ in
self?.endEditing(true)
}
.store(in: &self.subscriptions)
}
// in 커스텀 TextField
private func bind() {
self.keyboardToolbar.tapPublisher
.receive(on: RunLoop.main)
.sink { [weak self] in
self?.endEditing(true)
}
.store(in: &self.subscriptions)
}
private func setupToolbar() {
self.inputAccessoryView = self.keyboardToolbar
}
커스텀 객체에서 버튼 탭 시 endEditing이 호출된다면 viewController에서 번거롭게 keyboard down을 하지 않아도 되는 장점이 있다.