重构 MessageSettings

This commit is contained in:
Fin 2020-11-20 16:41:23 +08:00
parent 4cb6454087
commit aed16c5a1a
9 changed files with 246 additions and 57 deletions

View File

@ -83,6 +83,10 @@
06BBB88725650C6C0076F63E /* ArchiveSettingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5953224811505006B98F3 /* ArchiveSettingManager.swift */; };
06BBB89125650CCF0076F63E /* ArchiveSettingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5953224811505006B98F3 /* ArchiveSettingManager.swift */; };
06BBB896256518760076F63E /* NewServerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BBB895256518760076F63E /* NewServerViewModel.swift */; };
06BBB8B72567AC140076F63E /* MessageSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BBB8B62567AC140076F63E /* MessageSettingsViewModel.swift */; };
06BBB8BC2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BBB8BB2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift */; };
06BBB8C12567B3EF0076F63E /* BaseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BBB8C02567B3EF0076F63E /* BaseTableViewCell.swift */; };
06BBB8C92567B6730076F63E /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BBB8C82567B6730076F63E /* Operators.swift */; };
06C5952D2480E3F8006B98F3 /* LabelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5952C2480E3F8006B98F3 /* LabelCell.swift */; };
06C5952F248107F5006B98F3 /* iCloudStatusCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5952E248107F5006B98F3 /* iCloudStatusCell.swift */; };
06C5953124811392006B98F3 /* ArchiveSettingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C5953024811392006B98F3 /* ArchiveSettingCell.swift */; };
@ -203,6 +207,10 @@
06B1158E247BB1FB006D91FB /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = "<group>"; };
06B11590247BBC15006D91FB /* NotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationServiceExtension.entitlements; sourceTree = "<group>"; };
06BBB895256518760076F63E /* NewServerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewServerViewModel.swift; sourceTree = "<group>"; };
06BBB8B62567AC140076F63E /* MessageSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSettingsViewModel.swift; sourceTree = "<group>"; };
06BBB8BB2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveSettingCellViewModel.swift; sourceTree = "<group>"; };
06BBB8C02567B3EF0076F63E /* BaseTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTableViewCell.swift; sourceTree = "<group>"; };
06BBB8C82567B6730076F63E /* Operators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = "<group>"; };
06C5952C2480E3F8006B98F3 /* LabelCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelCell.swift; sourceTree = "<group>"; };
06C5952E248107F5006B98F3 /* iCloudStatusCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iCloudStatusCell.swift; sourceTree = "<group>"; };
06C5953024811392006B98F3 /* ArchiveSettingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArchiveSettingCell.swift; sourceTree = "<group>"; };
@ -262,6 +270,7 @@
0603706C20E23EC000F4CA05 /* BarkSFSafariViewController.swift */,
068F66B2247BD84C00DAD25A /* MessageListViewController.swift */,
06885EB5247FB9880004A303 /* MessageSettingsViewController.swift */,
06BBB8B62567AC140076F63E /* MessageSettingsViewModel.swift */,
060481ED250F404500BC9799 /* SoundsViewController.swift */,
065BE43F2563D649002A8CA4 /* SoundsViewModel.swift */,
);
@ -271,11 +280,13 @@
0604F7DC20620D3400B32F09 /* View */ = {
isa = PBXGroup;
children = (
06BBB8C02567B3EF0076F63E /* BaseTableViewCell.swift */,
0603706820E1F89500F4CA05 /* PreviewCardCell.swift */,
0667D191247D162C005DE2ED /* MessageTableViewCell.swift */,
06C5952C2480E3F8006B98F3 /* LabelCell.swift */,
06C5952E248107F5006B98F3 /* iCloudStatusCell.swift */,
06C5953024811392006B98F3 /* ArchiveSettingCell.swift */,
06BBB8BB2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift */,
06C595352481160F006B98F3 /* BKLabel.swift */,
060481EF250F51CA00BC9799 /* SoundCell.swift */,
065BE44F2563D939002A8CA4 /* SoundCellViewModel.swift */,
@ -355,6 +366,7 @@
0637FA7D20E0969800E80174 /* Client.swift */,
0667D193247D1BA0005DE2ED /* Date+Extension.swift */,
06C5953224811505006B98F3 /* ArchiveSettingManager.swift */,
06BBB8C82567B6730076F63E /* Operators.swift */,
);
path = Common;
sourceTree = "<group>";
@ -693,6 +705,7 @@
buildActionMask = 2147483647;
files = (
06BBB896256518760076F63E /* NewServerViewModel.swift in Sources */,
06BBB8C92567B6730076F63E /* Operators.swift in Sources */,
0603706920E1F89500F4CA05 /* PreviewCardCell.swift in Sources */,
0637FA8C20E0D7A700E80174 /* BaseViewController.swift in Sources */,
062B98C8251B27AE004562E7 /* UINavigationItem+Extension.swift in Sources */,
@ -722,11 +735,14 @@
06885EB6247FB9880004A303 /* MessageSettingsViewController.swift in Sources */,
06C5952D2480E3F8006B98F3 /* LabelCell.swift in Sources */,
0637FA7C20E0930E00E80174 /* BarkApi.swift in Sources */,
06BBB8BC2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift in Sources */,
0637FA8020E0981E00E80174 /* BarkSettings.swift in Sources */,
065BE4402563D649002A8CA4 /* SoundsViewModel.swift in Sources */,
065BE4462563D7E5002A8CA4 /* ViewModelType.swift in Sources */,
0603706B20E20A7C00F4CA05 /* String+Extension.swift in Sources */,
068F66B3247BD84C00DAD25A /* MessageListViewController.swift in Sources */,
06BBB8B72567AC140076F63E /* MessageSettingsViewModel.swift in Sources */,
06BBB8C12567B3EF0076F63E /* BaseTableViewCell.swift in Sources */,
0661A543204FDA4100965E4E /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

93
Common/Operators.swift Normal file
View File

@ -0,0 +1,93 @@
//
// Operators.swift
// RxExample
//
// Created by Krunoslav Zaher on 12/6/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
import RxSwift
import RxCocoa
#if os(iOS)
import UIKit
#elseif os(macOS)
import AppKit
#endif
// Two way binding operator between control property and relay, that's all it takes.
infix operator <-> : DefaultPrecedence
#if os(iOS)
func nonMarkedText(_ textInput: UITextInput) -> String? {
let start = textInput.beginningOfDocument
let end = textInput.endOfDocument
guard let rangeAll = textInput.textRange(from: start, to: end),
let text = textInput.text(in: rangeAll) else {
return nil
}
guard let markedTextRange = textInput.markedTextRange else {
return text
}
guard let startRange = textInput.textRange(from: start, to: markedTextRange.start),
let endRange = textInput.textRange(from: markedTextRange.end, to: end) else {
return text
}
return (textInput.text(in: startRange) ?? "") + (textInput.text(in: endRange) ?? "")
}
func <-> <Base>(textInput: TextInput<Base>, relay: BehaviorRelay<String>) -> Disposable {
let bindToUIDisposable = relay.bind(to: textInput.text)
let bindToRelay = textInput.text
.subscribe(onNext: { [weak base = textInput.base] n in
guard let base = base else {
return
}
let nonMarkedTextValue = nonMarkedText(base)
/**
In some cases `textInput.textRangeFromPosition(start, toPosition: end)` will return nil even though the underlying
value is not nil. This appears to be an Apple bug. If it's not, and we are doing something wrong, please let us know.
The can be reproed easily if replace bottom code with
if nonMarkedTextValue != relay.value {
relay.accept(nonMarkedTextValue ?? "")
}
and you hit "Done" button on keyboard.
*/
if let nonMarkedTextValue = nonMarkedTextValue, nonMarkedTextValue != relay.value {
relay.accept(nonMarkedTextValue)
}
}, onCompleted: {
bindToUIDisposable.dispose()
})
return Disposables.create(bindToUIDisposable, bindToRelay)
}
#endif
func <-> <T>(property: ControlProperty<T>, relay: BehaviorRelay<T>) -> Disposable {
if T.self == String.self {
#if DEBUG && !os(macOS)
fatalError("It is ok to delete this message, but this is here to warn that you are maybe trying to bind to some `rx.text` property directly to relay.\n" +
"That will usually work ok, but for some languages that use IME, that simplistic method could cause unexpected issues because it will return intermediate results while text is being inputed.\n" +
"REMEDY: Just use `textField <-> relay` instead of `textField.rx.text <-> relay`.\n" +
"Find out more here: https://github.com/ReactiveX/RxSwift/issues/649\n"
)
#endif
}
let bindToUIDisposable = relay.bind(to: property)
let bindToRelay = property
.subscribe(onNext: { n in
relay.accept(n)
}, onCompleted: {
bindToUIDisposable.dispose()
})
return Disposables.create(bindToUIDisposable, bindToRelay)
}

View File

@ -44,7 +44,7 @@ class MessageListViewController: UIViewController {
}
@objc func settingClick (){
self.navigationController?.pushViewController(MessageSettingsViewController(), animated: true)
self.navigationController?.pushViewController(MessageSettingsViewController(viewModel: MessageSettingsViewModel()), animated: true)
}
func refresh() {

View File

@ -8,7 +8,8 @@
import UIKit
import Material
class MessageSettingsViewController: UIViewController {
import RxDataSources
class MessageSettingsViewController: BaseViewController {
let tableView: UITableView = {
let tableView = UITableView()
tableView.separatorStyle = .none
@ -19,54 +20,44 @@ class MessageSettingsViewController: UIViewController {
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
override func makeUI() {
self.title = NSLocalizedString("settings")
self.view.addSubview(tableView)
tableView.dataSource = self
tableView.delegate = self
tableView.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
}
}
extension MessageSettingsViewController: UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 6
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: "\(LabelCell.self)") as! LabelCell
cell.textLabel?.text = "iCloud"
return cell
case 1:
let cell = tableView.dequeueReusableCell(withIdentifier: "\(iCloudStatusCell.self)") as! iCloudStatusCell
return cell
case 2:
let cell = tableView.dequeueReusableCell(withIdentifier: "\(LabelCell.self)") as! LabelCell
cell.textLabel?.text = NSLocalizedString("iCloudSync")
return cell
case 3:
let cell = tableView.dequeueReusableCell(withIdentifier: "\(LabelCell.self)") as! LabelCell
cell.textLabel?.text = NSLocalizedString("defaultArchiveSettings")
return cell
case 4:
let cell = tableView.dequeueReusableCell(withIdentifier: "\(ArchiveSettingCell.self)") as! ArchiveSettingCell
return cell
case 5:
let cell = tableView.dequeueReusableCell(withIdentifier: "\(LabelCell.self)") as! LabelCell
cell.textLabel?.text = NSLocalizedString("archiveNote")
return cell
default:
return UITableViewCell()
}
}
override func bindViewModel() {
guard let viewModel = self.viewModel as? MessageSettingsViewModel else {
return
}
let output = viewModel.transform(input: MessageSettingsViewModel.Input())
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, MessageSettingItem>> { (source, tableView, indexPath, item) -> UITableViewCell in
switch item {
case .label(let text):
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(LabelCell.self)") as? LabelCell {
cell.textLabel?.text = text
return cell
}
case .iCloudStatus:
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(iCloudStatusCell.self)") {
return cell
}
case .archiveSetting(let viewModel):
if let cell = tableView.dequeueReusableCell(withIdentifier: "\(ArchiveSettingCell.self)") as? ArchiveSettingCell {
cell.bindViewModel(model: viewModel)
return cell
}
}
return UITableViewCell()
}
output.settings
.drive(tableView.rx.items(dataSource: dataSource))
.disposed(by: rx.disposeBag)
}
}

View File

@ -0,0 +1,56 @@
//
// MessageSettingsViewModel.swift
// Bark
//
// Created by huangfeng on 2020/11/20.
// Copyright © 2020 Fin. All rights reserved.
//
import Foundation
import RxSwift
import RxCocoa
import RxDataSources
class MessageSettingsViewModel: ViewModel, ViewModelType {
struct Input {
}
struct Output {
var settings:Driver<[SectionModel<String, MessageSettingItem>]>
}
func transform(input: Input) -> Output {
let settings:[MessageSettingItem] = {
var settings = [MessageSettingItem]()
settings.append(.label(text: "iCloud"))
settings.append(.iCloudStatus)
settings.append(.label(text: NSLocalizedString("iCloudSync")))
settings.append(.label(text: NSLocalizedString("defaultArchiveSettings")))
settings.append(.archiveSetting(viewModel: ArchiveSettingCellViewModel(on: ArchiveSettingManager.shared.isArchive)))
settings.append(.label(text: NSLocalizedString("archiveNote")))
return settings
}()
settings.compactMap { (item) -> ArchiveSettingCellViewModel? in
if case let MessageSettingItem.archiveSetting(viewModel) = item {
return viewModel
}
return nil
}
.first?
.on
.subscribe(onNext: { (on) in
ArchiveSettingManager.shared.isArchive = on
}).disposed(by: rx.disposeBag)
return Output(settings: Driver<[SectionModel<String, MessageSettingItem>]>
.just([SectionModel(model: "model", items: settings)]))
}
}
enum MessageSettingItem {
case label(text:String)
case iCloudStatus
case archiveSetting(viewModel:ArchiveSettingCellViewModel)
}

View File

@ -8,7 +8,7 @@
import UIKit
class ArchiveSettingCell: UITableViewCell {
class ArchiveSettingCell: BaseTableViewCell {
let switchButton: UISwitch = {
let btn = UISwitch()
return btn
@ -24,17 +24,18 @@ class ArchiveSettingCell: UITableViewCell {
make.right.equalToSuperview().offset(-16)
make.centerY.equalToSuperview()
}
switchButton.isOn = ArchiveSettingManager.shared.isArchive
switchButton.addTarget(self, action: #selector(switchToggle(sender:)), for: .valueChanged)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func switchToggle(sender:UISwitch) {
ArchiveSettingManager.shared.isArchive = sender.isOn
override func bindViewModel(model: ViewModel) {
super.bindViewModel(model: model)
guard let viewModel = model as? ArchiveSettingCellViewModel else {
return
}
(self.switchButton.rx.isOn <-> viewModel.on)
.disposed(by: rx.reuseBag)
}
}

View File

@ -0,0 +1,17 @@
//
// ArchiveSettingCellViewModel.swift
// Bark
//
// Created by huangfeng on 2020/11/20.
// Copyright © 2020 Fin. All rights reserved.
//
import Foundation
import RxCocoa
class ArchiveSettingCellViewModel: ViewModel {
var on: BehaviorRelay<Bool>
init(on:Bool) {
self.on = BehaviorRelay<Bool>(value: on)
super.init()
}
}

View File

@ -0,0 +1,16 @@
//
// BaseTableViewCell.swift
// Bark
//
// Created by huangfeng on 2020/11/20.
// Copyright © 2020 Fin. All rights reserved.
//
import UIKit
class BaseTableViewCell: UITableViewCell {
var viewModel:ViewModel?
func bindViewModel(model:ViewModel){
self.viewModel = model
}
}

View File

@ -10,7 +10,7 @@ import UIKit
import Material
import AVKit
class SoundCell: UITableViewCell {
class SoundCell: BaseTableViewCell {
let copyButton = IconButton(image: UIImage(named: "baseline_file_copy_white_24pt"), tintColor: Color.grey.base)
let nameLabel:UILabel = {
let label = UILabel()
@ -50,12 +50,11 @@ class SoundCell: UITableViewCell {
fatalError("init(coder:) has not been implemented")
}
var viewModel:ViewModel?
func bindViewModel(model:ViewModel){
override func bindViewModel(model:ViewModel){
super.bindViewModel(model: model)
guard let viewModel = model as? SoundCellViewModel else {
return
}
self.viewModel = model
viewModel.name
.bind(to: nameLabel.rx.text)