본문 바로가기

iOS 개발/번역기 앱(RxSwift)

번역기 앱 - 8. 언어 선택 View

반응형

안녕하세요. Skillist입니다

 

아침부터 글을 작성하다보니, 어느덧 오후네요;;;;;;;;

이렇게 연휴는 지나가나봅니다.

뭐 하루를 잘 사용했으면 된거죠.

 

언어 선택 View를 구현해볼게요.

버튼 3개만 구현하면 되겠죠?

 

———————————————————————————————————————————————————

 

viewModel을 볼게요.

 

13라인 : disposeBag입니다.

16~18라인 : 버튼 터치에 대한 이벤트를 받습니다.

20~20라인 : view에서 언어를 변경하면, 변경된 언어 이벤트를 받습니다.

 

24~24라인 : Driver로, 변경된 언어를 전달합니다.

27, 35라인 : asDriver를 통해 Relay를 Deiver로 변경합니다. Driver는 error가 없고 Main쓰레드에서 수행되며 하나의 시퀀스를 share합니다. 따라서 UI관련 작업에 사용하기 좋습니다.



언어가 변경되면, label의 title도 변경해야 합니다.

언어를 변경하면 하나의 label이 아니라, 2개의 label입니다.

하나의 시퀀스를 공유하여 2개의 label을 변경하기에 딱 좋죠.

Driver의 친구로는 Signal이 있습니다. 한번 찾아보세요.

 

———————————————————————————————————————————————————

 

이번엔 View를 살펴보겠습니다.

 

view에서 사용할, UI컴포넌트 입니다.

버튼 3개를 구현하고, stackView에 추가합니다.

또, 스냅킷을 통해서 레이아웃 제약사항을 구성했습니다.

 

드디어 가장 중요한 bind입니다.

 

69라인 : sourceLanguage가 변경되면, 버튼 title을 변경된 title(text)로 변경합니다.

76라인 : targetLanguage가 변경되면, 버튼 title을 변경된 title(text)로 변경합니다.

83라인 : 버튼이 터치되면 이벤트를 viewModel로 방출합니다.

87라인 : 버튼이 터치되면 이벤트를 viewModel로 방출합니다.

91라인 : 2개 옵저버블의 최신값(선택된 source와 target의 언어)을 묶어서 방출합니다.

96라인 : change 버튼 터치 시,

withLatestFrom를 통해 91라인의 최근 선택된 언어들을 가져와서 언어를 변경합니다.

 

———————————————————————————————————————————————————

 

이렇게 view와 viewModel을 구현해봤어요.

바인딩은 번역 View에서 구현할겁니다.

이 정도면 충분해요. 이해까지 됐다면 아주 아주 충분합니다.

 

오늘도 고생하셨구요, 다음 글에서 만나겠습니다.

 

 

잘못되거나 부족한 내용 등, 피드백 감사합니다!

 

https://github.com/DeveloperSkillist/TranstorKing

 

GitHub - DeveloperSkillist/TranstorKing

Contribute to DeveloperSkillist/TranstorKing development by creating an account on GitHub.

github.com

 

 

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓  전체 코드  ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

struct SelectLanguageViewModel {
    let disposeBag = DisposeBag()
    
    //view -> viewModel
    var sourceLanguageButtonTap = PublishRelay<Void>()
    var targetLanguageButtonTap = PublishRelay<Void>()
    
    var changedSourceLanguage = PublishRelay<Language>()
    var changedTargetLanguage = PublishRelay<Language>()
    
    //viewModel -> view
    let sourceLanguage: Driver<Language>
    let targetLanguage: Driver<Language>
    
    init() {
        sourceLanguage = changedSourceLanguage
            .distinctUntilChanged()
            .startWith(.ko)
            .asDriver(onErrorJustReturn: .ko)
        
        targetLanguage = changedTargetLanguage
            .distinctUntilChanged()
            .startWith(.en)
            .asDriver(onErrorJustReturn: .en)
    }
}
class SelectLanguageView: UIView {
    let disposeBag = DisposeBag()
    
    private lazy var sourceLanguageButton: UIButton = {
        var button = UIButton()
        button.setTitle(Language.ko.title, for: .normal)
        button.setTitleColor(.label, for: .normal)
        button.backgroundColor = .systemBackground
        button.layer.cornerRadius = 10
        return button
    }()
    
    private lazy var changeLanguageButton: UIButton = {
        var button = UIButton()
        button.setImage(UIImage(systemName: "repeat"), for: .normal)
        button.setTitleColor(.label, for: .normal)
        button.backgroundColor = .systemBackground
        button.layer.cornerRadius = 10
        button.tintColor = .label
        return button
    }()
    
    private lazy var targetLanguageButton: UIButton = {
        var button = UIButton()
        button.setTitle(Language.en.title, for: .normal)
        button.setTitleColor(.label, for: .normal)
        button.backgroundColor = .systemBackground
        button.layer.cornerRadius = 10
        return button
    }()
    
    private lazy var stackView: UIStackView = {
        var stackView = UIStackView()
        stackView.distribution = .fillEqually
        stackView.spacing = 16
        
        [
            sourceLanguageButton,
            changeLanguageButton,
            targetLanguageButton
        ].forEach {
            stackView.addArrangedSubview($0)
        }
        return stackView
    }()
    
    override init(frame: CGRect) {
        super.init(frame: .zero)
        
        layout()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func bind(_ viewModel: SelectLanguageViewModel) {
        viewModel.sourceLanguage
            .map {
                $0.title
            }
            .drive(sourceLanguageButton.rx.title(for: .normal))
            .disposed(by: disposeBag)
        
        viewModel.targetLanguage
            .map {
                $0.title
            }
            .drive(targetLanguageButton.rx.title(for: .normal))
            .disposed(by: disposeBag)
        
        sourceLanguageButton.rx.tap
            .bind(to: viewModel.sourceLanguageButtonTap)
            .disposed(by: disposeBag)
        
        targetLanguageButton.rx.tap
            .bind(to: viewModel.targetLanguageButtonTap)
            .disposed(by: disposeBag)
        
        let latestLanguages = Observable.combineLatest(
            viewModel.sourceLanguage.asObservable(),
            viewModel.targetLanguage.asObservable()
            )
        
        changeLanguageButton.rx.tap
            .withLatestFrom(latestLanguages) { ($1.0, $1.1) }
            .bind(onNext: { sourceLan, targetLan in
                viewModel.changedSourceLanguage.accept(targetLan)
                viewModel.changedTargetLanguage.accept(sourceLan)
            })
            .disposed(by: disposeBag)
    }
    
    private func layout() {
        addSubview(stackView)
        
        stackView.snp.makeConstraints {
            $0.edges.equalToSuperview()
            $0.top.equalToSuperview().inset(16)
            $0.height.equalTo(50)
        }
    }
}
반응형