Xib & RxSwiftを用いて実装します。
Contents 非表示
実装
UI作り (Xib)
アコーディオン部分はわかりやすく色を変えてます、お好きにカスタマイズしてください。
全体をStackViewで入れて、二つのUIViewで制御する感じです。
ViewController
import RxCocoa
import RxSwift
import UIKit
final class HomeViewController: UIViewController {
// MARK: - Properties
@IBOutlet private weak var accrodionButton: UIButton!
@IBOutlet private weak var accrodionView: UIView!
@IBOutlet private weak var accordionViewHeightConstraint: NSLayoutConstraint!
private let viewModel = HomeViewModel()
private let disposeBag = DisposeBag()
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
bind()
}
}
// MARK: - Bind
private extension HomeViewController {
func bind() {
accrodionButton.rx.tap.asSignal()
.emit(to: viewModel.inputs.switchAccordion)
.disposed(by: disposeBag)
viewModel.outputs.isAccordionViewHidden
.drive(onNext: { [weak self] isAccordionViewHidden in
guard let self = self else { return }
if !isAccordionViewHidden {
self.accrodionView.isHidden = false
}
UIView.animate(withDuration: 0.3, animations: {
self.accordionViewHeightConstraint.constant = isAccordionViewHidden ? 0 : 160
self.view.layoutIfNeeded()
self.accrodionView.alpha = isAccordionViewHidden ? 0.0 : 1.0
}, completion: { _ in
if isAccordionViewHidden {
self.accrodionView.isHidden = true
}
})
})
.disposed(by: disposeBag)
}
}
ViewModel
import RxCocoa
import RxSwift
protocol HomeViewModelInputs: AnyObject {
var switchAccordion: PublishRelay<Void> { get }
}
protocol HomeViewModelOutputs: AnyObject {
var isAccordionViewHidden: Driver<Bool> { get }
}
protocol HomeViewModelType: AnyObject {
var inputs: HomeViewModelInputs { get }
var outputs: HomeViewModelOutputs { get }
}
final class HomeViewModel: HomeViewModelType, HomeViewModelInputs, HomeViewModelOutputs {
// MARK: - Properties
var inputs: HomeViewModelInputs { return self }
var outputs: HomeViewModelOutputs { return self }
// MARK: - Input Sources
let switchAccordion = PublishRelay<Void>()
// MARK: - Output Sources
let isAccordionViewHidden: Driver<Bool>
// MARK: - Properties
private let _isAccordionViewHidden = BehaviorRelay<Bool>(value: true)
private let disposeBag = DisposeBag()
// MARK: - Initialize
init() {
self.isAccordionViewHidden = _isAccordionViewHidden.asDriver(onErrorDriveWith: .empty())
switchAccordion.asObservable()
.withLatestFrom(self.isAccordionViewHidden)
.map { !$0 }
.bind(to: _isAccordionViewHidden)
.disposed(by: disposeBag)
}
}