From b1e2f527e9e59ecfcc84132c3440f90601e0e2fd Mon Sep 17 00:00:00 2001 From: Fin Date: Wed, 18 Nov 2020 15:25:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=BF=E6=8D=A2=E4=B8=BAMVVM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bark.xcodeproj/project.pbxproj | 24 +++++ Common/Reusable.swift | 49 ++++++++++ Common/ViewModelType.swift | 19 ++++ Controller/BaseViewController.swift | 26 +++++- Controller/HomeViewController.swift | 4 +- Controller/MessageListViewController.swift | 2 +- .../MessageSettingsViewController.swift | 2 +- Controller/NewServerViewController.swift | 2 +- Controller/SoundsViewController.swift | 91 +++++++++++-------- Controller/SoundsViewModel.swift | 49 ++++++++++ Podfile | 7 ++ Podfile.lock | 37 +++++++- View/SoundCell.swift | 28 ++++-- View/SoundCellViewModel.swift | 27 ++++++ 14 files changed, 313 insertions(+), 54 deletions(-) create mode 100644 Common/Reusable.swift create mode 100644 Common/ViewModelType.swift create mode 100644 Controller/SoundsViewModel.swift create mode 100644 View/SoundCellViewModel.swift diff --git a/Bark.xcodeproj/project.pbxproj b/Bark.xcodeproj/project.pbxproj index c978818..74f693c 100644 --- a/Bark.xcodeproj/project.pbxproj +++ b/Bark.xcodeproj/project.pbxproj @@ -63,6 +63,10 @@ 0637FA8C20E0D7A700E80174 /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0637FA8B20E0D7A700E80174 /* BaseViewController.swift */; }; 063C499520E36BF9001BCA35 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 063C499720E36BF9001BCA35 /* Localizable.strings */; }; 065A4D4220EE1A31002EB2DB /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 063C499720E36BF9001BCA35 /* Localizable.strings */; }; + 065BE4402563D649002A8CA4 /* SoundsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065BE43F2563D649002A8CA4 /* SoundsViewModel.swift */; }; + 065BE4462563D7E5002A8CA4 /* ViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065BE4452563D7E5002A8CA4 /* ViewModelType.swift */; }; + 065BE44B2563D8E1002A8CA4 /* Reusable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065BE44A2563D8E1002A8CA4 /* Reusable.swift */; }; + 065BE4502563D939002A8CA4 /* SoundCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065BE44F2563D939002A8CA4 /* SoundCellViewModel.swift */; }; 0661A543204FDA4100965E4E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0661A542204FDA4100965E4E /* AppDelegate.swift */; }; 0661A545204FDA4100965E4E /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0661A544204FDA4100965E4E /* HomeViewController.swift */; }; 0661A54A204FDA4100965E4E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0661A549204FDA4100965E4E /* Assets.xcassets */; }; @@ -175,6 +179,10 @@ 0637FA8B20E0D7A700E80174 /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = ""; }; 063C499620E36BF9001BCA35 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 063C499820E36C15001BCA35 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; + 065BE43F2563D649002A8CA4 /* SoundsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundsViewModel.swift; sourceTree = ""; }; + 065BE4452563D7E5002A8CA4 /* ViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModelType.swift; sourceTree = ""; }; + 065BE44A2563D8E1002A8CA4 /* Reusable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = ""; }; + 065BE44F2563D939002A8CA4 /* SoundCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundCellViewModel.swift; sourceTree = ""; }; 0661A53F204FDA4100965E4E /* Bark.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Bark.app; sourceTree = BUILT_PRODUCTS_DIR; }; 0661A542204FDA4100965E4E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 0661A544204FDA4100965E4E /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = ""; }; @@ -249,6 +257,7 @@ 068F66B2247BD84C00DAD25A /* MessageListViewController.swift */, 06885EB5247FB9880004A303 /* MessageSettingsViewController.swift */, 060481ED250F404500BC9799 /* SoundsViewController.swift */, + 065BE43F2563D649002A8CA4 /* SoundsViewModel.swift */, ); path = Controller; sourceTree = ""; @@ -263,6 +272,7 @@ 06C5953024811392006B98F3 /* ArchiveSettingCell.swift */, 06C595352481160F006B98F3 /* BKLabel.swift */, 060481EF250F51CA00BC9799 /* SoundCell.swift */, + 065BE44F2563D939002A8CA4 /* SoundCellViewModel.swift */, 062B98C2251B2762004562E7 /* BKButton.swift */, 062B98C7251B27AE004562E7 /* UINavigationItem+Extension.swift */, ); @@ -336,10 +346,20 @@ path = NotificationContentExtension; sourceTree = ""; }; + 065BE4442563D7AA002A8CA4 /* Common */ = { + isa = PBXGroup; + children = ( + 065BE4452563D7E5002A8CA4 /* ViewModelType.swift */, + 065BE44A2563D8E1002A8CA4 /* Reusable.swift */, + ); + path = Common; + sourceTree = ""; + }; 0661A536204FDA4100965E4E = { isa = PBXGroup; children = ( 063204EF250B6DC2001561EC /* Sounds */, + 065BE4442563D7AA002A8CA4 /* Common */, 0604F7DD20620D3800B32F09 /* Model */, 0604F7DC20620D3400B32F09 /* View */, 0604F7DB20620D2700B32F09 /* Controller */, @@ -664,12 +684,14 @@ 060481EE250F404500BC9799 /* SoundsViewController.swift in Sources */, 0603706D20E23EC000F4CA05 /* BarkSFSafariViewController.swift in Sources */, 06C5953124811392006B98F3 /* ArchiveSettingCell.swift in Sources */, + 065BE4502563D939002A8CA4 /* SoundCellViewModel.swift in Sources */, 06B1158F247BB1FB006D91FB /* Message.swift in Sources */, 06C595362481160F006B98F3 /* BKLabel.swift in Sources */, 0637FA7820E0926D00E80174 /* BarkTargetType.swift in Sources */, 0637FA7E20E0969800E80174 /* Client.swift in Sources */, 0661A545204FDA4100965E4E /* HomeViewController.swift in Sources */, 06C5952F248107F5006B98F3 /* iCloudStatusCell.swift in Sources */, + 065BE44B2563D8E1002A8CA4 /* Reusable.swift in Sources */, 0637FA8620E0AB6600E80174 /* UIColor+Extension.swift in Sources */, 0637FA8A20E0D58800E80174 /* NewServerViewController.swift in Sources */, 0637FA8220E09C4B00E80174 /* BarkNavigationController.swift in Sources */, @@ -685,6 +707,8 @@ 06C5952D2480E3F8006B98F3 /* LabelCell.swift in Sources */, 0637FA7C20E0930E00E80174 /* BarkApi.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 */, 0661A543204FDA4100965E4E /* AppDelegate.swift in Sources */, diff --git a/Common/Reusable.swift b/Common/Reusable.swift new file mode 100644 index 0000000..6a2519f --- /dev/null +++ b/Common/Reusable.swift @@ -0,0 +1,49 @@ +// +// Reusable.swift +// Bark +// +// Created by huangfeng on 2020/11/17. +// Copyright © 2020 Fin. All rights reserved. +// + +import UIKit +import RxSwift +import RxCocoa + +private var prepareForReuseBag: Int8 = 0 + +@objc public protocol Reusable : class { + func prepareForReuse() +} + +extension UITableViewCell: Reusable {} +extension UITableViewHeaderFooterView: Reusable {} +extension UICollectionReusableView: Reusable {} + +extension Reactive where Base: Reusable { + var prepareForReuse: Observable { + return Observable.of(sentMessage(#selector(Base.prepareForReuse)).map { _ in }, deallocated).merge() + } + + var reuseBag: DisposeBag { + MainScheduler.ensureExecutingOnScheduler() + + if let bag = objc_getAssociatedObject(base, &prepareForReuseBag) as? DisposeBag { + return bag + } + + let bag = DisposeBag() + objc_setAssociatedObject(base, &prepareForReuseBag, bag, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) + + _ = sentMessage(#selector(Base.prepareForReuse)) + .subscribe(onNext: { [weak base] _ in + guard let strongBase = base else { + return + } + let newBag = DisposeBag() + objc_setAssociatedObject(strongBase, &prepareForReuseBag, newBag, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) + }) + + return bag + } +} diff --git a/Common/ViewModelType.swift b/Common/ViewModelType.swift new file mode 100644 index 0000000..ddd3fb6 --- /dev/null +++ b/Common/ViewModelType.swift @@ -0,0 +1,19 @@ +// +// ViewModelType.swift +// Bark +// +// Created by huangfeng on 2020/11/17. +// Copyright © 2020 Fin. All rights reserved. +// + +import Foundation +import RxSwift + +protocol ViewModelType { + associatedtype Input + associatedtype Output + + func transform(input: Input) -> Output +} + +class ViewModel:NSObject{ } diff --git a/Controller/BaseViewController.swift b/Controller/BaseViewController.swift index 5729ba9..3298c6b 100644 --- a/Controller/BaseViewController.swift +++ b/Controller/BaseViewController.swift @@ -9,15 +9,35 @@ import UIKit import Material class BaseViewController: UIViewController { - - override func viewDidLoad() { - super.viewDidLoad() + + let viewModel:ViewModel + init(viewModel:ViewModel) { + self.viewModel = viewModel + super.init(nibName: nil, bundle: nil) + self.view.backgroundColor = Color.grey.lighten5 } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override var preferredStatusBarStyle: UIStatusBarStyle{ get { return .lightContent } } + + override func viewDidLoad() { + super.viewDidLoad() + makeUI() + bindViewModel() + } + + func makeUI() { + + } + func bindViewModel(){ + + } } diff --git a/Controller/HomeViewController.swift b/Controller/HomeViewController.swift index 0d0000a..0eac32d 100644 --- a/Controller/HomeViewController.swift +++ b/Controller/HomeViewController.swift @@ -9,7 +9,7 @@ import UIKit import UserNotifications import Material -class HomeViewController: BaseViewController { +class HomeViewController: UIViewController { let newButton: BKButton = { let btn = BKButton() @@ -42,7 +42,7 @@ class HomeViewController: BaseViewController { notice: NSLocalizedString("setSounds"), queryParameter: "sound=minuet", moreInfo:NSLocalizedString("viewAllSounds"), - moreViewController: SoundsViewController() + moreViewController: SoundsViewController(viewModel: SoundsViewModel()) ), PreviewModel( body: NSLocalizedString("archiveNotificationMessageTitle"), diff --git a/Controller/MessageListViewController.swift b/Controller/MessageListViewController.swift index e0f1b37..35a99c2 100644 --- a/Controller/MessageListViewController.swift +++ b/Controller/MessageListViewController.swift @@ -10,7 +10,7 @@ import UIKit import Material import RealmSwift -class MessageListViewController: BaseViewController { +class MessageListViewController: UIViewController { let tableView: UITableView = { let tableView = UITableView() tableView.separatorStyle = .none diff --git a/Controller/MessageSettingsViewController.swift b/Controller/MessageSettingsViewController.swift index c3fa353..b4b34ec 100644 --- a/Controller/MessageSettingsViewController.swift +++ b/Controller/MessageSettingsViewController.swift @@ -8,7 +8,7 @@ import UIKit import Material -class MessageSettingsViewController: BaseViewController { +class MessageSettingsViewController: UIViewController { let tableView: UITableView = { let tableView = UITableView() tableView.separatorStyle = .none diff --git a/Controller/NewServerViewController.swift b/Controller/NewServerViewController.swift index f4bfa6d..2430901 100644 --- a/Controller/NewServerViewController.swift +++ b/Controller/NewServerViewController.swift @@ -10,7 +10,7 @@ import UIKit import Material import SnapKit import SafariServices -class NewServerViewController: BaseViewController { +class NewServerViewController: UIViewController { let addressTextField : TextField = { let textField = TextField() diff --git a/Controller/SoundsViewController.swift b/Controller/SoundsViewController.swift index 2922340..69d3afd 100644 --- a/Controller/SoundsViewController.swift +++ b/Controller/SoundsViewController.swift @@ -10,55 +10,70 @@ import UIKit import Material import AVKit -class SoundsViewController: UIViewController,UITableViewDelegate,UITableViewDataSource { +import RxSwift +import RxCocoa +import RxDataSources +import NSObject_Rx + +class SoundsViewController: BaseViewController { let tableView: UITableView = { let tableView = UITableView() tableView.backgroundColor = Color.grey.lighten5 - tableView.register(UITableViewCell.self, forCellReuseIdentifier: "\(SoundCell.self)") + tableView.register(SoundCell.self, forCellReuseIdentifier: "\(SoundCell.self)") return tableView }() - let audios:[AVURLAsset] = { - var urls = Bundle.main.urls(forResourcesWithExtension: "caf", subdirectory: nil) ?? [] - urls.sort { (u1, u2) -> Bool in - u1.lastPathComponent.localizedStandardCompare(u2.lastPathComponent) == ComparisonResult.orderedAscending - } - let audios = urls.map { (url) -> AVURLAsset in - let asset = AVURLAsset(url: url) - return asset - } - return audios - }() - override func viewDidLoad() { - super.viewDidLoad() + + override func makeUI() { self.title = NSLocalizedString("notificationSound") - self.tableView.dataSource = self - self.tableView.delegate = self + self.view.addSubview(self.tableView) self.tableView.snp.makeConstraints { (make) in make.edges.equalToSuperview() } - - let header = UILabel() - header.fontSize = 12 - header.text = " \(NSLocalizedString("previewSound"))" - header.textColor = Color.darkText.secondary - header.frame = CGRect(x: 0, y: 0, width: 0, height: 40) - self.tableView.tableHeaderView = header + + self.tableView.tableHeaderView = { + let header = UILabel() + header.fontSize = 12 + header.text = " \(NSLocalizedString("previewSound"))" + header.textColor = Color.darkText.secondary + header.frame = CGRect(x: 0, y: 0, width: 0, height: 40) + return header + }() } - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return audios.count - } - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "\(SoundCell.self)") as? SoundCell ?? SoundCell() - cell.nameLabel.text = audios[indexPath.row].url.deletingPathExtension().lastPathComponent - cell.durationLabel.text = "\(String(format: "%.2g", CMTimeGetSeconds(audios[indexPath.row].duration))) second(s)"; - return cell - } - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - var soundID:SystemSoundID = 0 - AudioServicesCreateSystemSoundID(audios[indexPath.row].url as CFURL, &soundID) - AudioServicesPlaySystemSoundWithCompletion(soundID) { - AudioServicesDisposeSystemSoundID(soundID) + override func bindViewModel() { + guard let viewModel = viewModel as? SoundsViewModel else { + return } + + let output = viewModel.transform( + input: SoundsViewModel.Input(soundSelected: self.tableView.rx + .modelSelected(SoundCellViewModel.self) + .asDriver())) + + let dataSource = RxTableViewSectionedReloadDataSource> { (source, tableView, indexPath, item) -> UITableViewCell in + guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(SoundCell.self)") as? SoundCell else { + return UITableViewCell() + } + cell.bindViewModel(model: item) + return cell + } + + output.audios + .map{ [SectionModel(model: "model", items: $0)] } + .bind(to: tableView.rx.items(dataSource: dataSource)) + .disposed(by: rx.disposeBag) + + output.copyNameAction.drive(onNext: { name in + UIPasteboard.general.string = name.trimmingCharacters(in: .whitespacesAndNewlines) + Client.shared.currentNavigationController?.showSnackbar(text: NSLocalizedString("Copy")) + }).disposed(by: rx.disposeBag) + + output.playAction.drive(onNext: { url in + var soundID:SystemSoundID = 0 + AudioServicesCreateSystemSoundID(url, &soundID) + AudioServicesPlaySystemSoundWithCompletion(soundID) { + AudioServicesDisposeSystemSoundID(soundID) + } + }).disposed(by: rx.disposeBag) } } diff --git a/Controller/SoundsViewModel.swift b/Controller/SoundsViewModel.swift new file mode 100644 index 0000000..bec4507 --- /dev/null +++ b/Controller/SoundsViewModel.swift @@ -0,0 +1,49 @@ +// +// SoundsViewModel.swift +// Bark +// +// Created by huangfeng on 2020/11/17. +// Copyright © 2020 Fin. All rights reserved. +// + +import Foundation +import RxSwift +import RxCocoa +import AVKit + +class SoundsViewModel:ViewModel,ViewModelType { + + struct Input { + var soundSelected:Driver + } + struct Output { + var audios:Observable<[SoundCellViewModel]> + var copyNameAction: Driver + var playAction: Driver + } + + func transform(input: Input) -> Output { + let models = { () -> [AVURLAsset] in + var urls = Bundle.main.urls(forResourcesWithExtension: "caf", subdirectory: nil) ?? [] + urls.sort { (u1, u2) -> Bool in + u1.lastPathComponent.localizedStandardCompare(u2.lastPathComponent) == ComparisonResult.orderedAscending + } + let audios = urls.map { (url) -> AVURLAsset in + let asset = AVURLAsset(url: url) + return asset + } + return audios + }().map { SoundCellViewModel(model: $0 ) } + + let copyAction = Driver.merge( + models.map { $0.copyNameAction.asDriver(onErrorDriveWith: .empty()) } + ).asDriver() + + return Output( + audios: Observable.just(models), + copyNameAction: copyAction, + playAction: input.soundSelected.map{ $0.model.url as CFURL } + ) + } + +} diff --git a/Podfile b/Podfile index 9d542b1..662e4f2 100644 --- a/Podfile +++ b/Podfile @@ -15,6 +15,13 @@ def pods pod 'DeviceKit' pod 'DefaultsKit', :git => 'https://github.com/nmdias/DefaultsKit' pod 'IceCream' + + pod 'RxSwift' + pod 'RxCocoa' + pod 'RxGesture' + pod 'RxDataSources' + pod 'NSObject+Rx' + end target 'Bark' do diff --git a/Podfile.lock b/Podfile.lock index 49a665e..cb114cc 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -2,6 +2,7 @@ PODS: - Alamofire (5.2.0) - DefaultsKit (0.2.0) - DeviceKit (3.2.0) + - Differentiator (4.0.1) - FDFullscreenPopGesture (1.1) - IceCream (1.13.1): - RealmSwift @@ -18,12 +19,26 @@ PODS: - Moya/RxSwift (14.0.0): - Moya/Core - RxSwift (~> 5.0) + - "NSObject+Rx (5.1.0)": + - RxSwift (~> 5.1) - ObjectMapper (4.2.0) - Realm (5.0.0): - Realm/Headers (= 5.0.0) - Realm/Headers (5.0.0) - RealmSwift (5.0.0): - Realm (= 5.0.0) + - RxCocoa (5.1.1): + - RxRelay (~> 5) + - RxSwift (~> 5) + - RxDataSources (4.0.1): + - Differentiator (~> 4.0) + - RxCocoa (~> 5.0) + - RxSwift (~> 5.0) + - RxGesture (3.0.2): + - RxCocoa (~> 5.1) + - RxSwift (~> 5.1) + - RxRelay (5.1.1): + - RxSwift (~> 5) - RxSwift (5.1.1) - SnapKit (5.0.1) - SVProgressHUD (2.2.5) @@ -37,12 +52,24 @@ DEPENDENCIES: - KVOController - Material - Moya/RxSwift + - "NSObject+Rx" - ObjectMapper + - RxCocoa + - RxDataSources + - RxGesture + - RxSwift - SnapKit - SVProgressHUD - SwiftyJSON SPEC REPOS: + https://github.com/CocoaPods/Specs.git: + - Differentiator + - "NSObject+Rx" + - RxCocoa + - RxDataSources + - RxGesture + - RxRelay trunk: - Alamofire - DeviceKit @@ -73,20 +100,26 @@ SPEC CHECKSUMS: Alamofire: c1ca147559e730bfb2182c8c7aafbdd90a867987 DefaultsKit: 6c767941b2c3fe34c4d70e300e6c3b80c94964dd DeviceKit: d081659419cce07c0b5239dbc9fb39ed7413c7fe + Differentiator: 886080237d9f87f322641dedbc5be257061b0602 FDFullscreenPopGesture: a8a620179e3d9c40e8e00256dcee1c1a27c6d0f0 IceCream: 0447d87b55df85651dd60b15712cef64dbe1cb54 KVOController: d72ace34afea42468329623b3379ab3cd1d286b6 Material: a2a3f400a3b549d53ef89e56c58c4535b29db387 Motion: cf1e060e489f6661126374d5c60dbd2ed991605c Moya: 5b45dacb75adb009f97fde91c204c1e565d31916 + "NSObject+Rx": fa6bbcc1ab1faa06b01466bc09b1e0692bbc5946 ObjectMapper: 1eb41f610210777375fa806bf161dc39fb832b81 Realm: 76066d333de26f97e9caedaa85ee3ff4a62ca265 RealmSwift: c1bdff09b422569dc6881410d9ccf712bcdb79b0 + RxCocoa: 32065309a38d29b5b0db858819b5bf9ef038b601 + RxDataSources: efee07fa4de48477eca0a4611e6d11e2da9c1114 + RxGesture: d6bd7447ca3a596c7a9702a6a2b6a2bb5d8bae54 + RxRelay: d77f7d771495f43c556cbc43eebd1bb54d01e8e9 RxSwift: 81470a2074fa8780320ea5fe4102807cb7118178 SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6 SwiftyJSON: 36413e04c44ee145039d332b4f4e2d3e8d6c4db7 -PODFILE CHECKSUM: 90c7dcd577fc40757291126890d6791408bb905a +PODFILE CHECKSUM: f0191b272c872b4d43da19a114ad4be556fb7eba -COCOAPODS: 1.8.4 +COCOAPODS: 1.10.0 diff --git a/View/SoundCell.swift b/View/SoundCell.swift index c845c06..a3f4012 100644 --- a/View/SoundCell.swift +++ b/View/SoundCell.swift @@ -8,6 +8,7 @@ import UIKit import Material +import AVKit class SoundCell: UITableViewCell { let copyButton = IconButton(image: UIImage(named: "baseline_file_copy_white_24pt"), tintColor: Color.grey.base) @@ -29,6 +30,8 @@ class SoundCell: UITableViewCell { self.contentView.addSubview(nameLabel) self.contentView.addSubview(durationLabel) + self.contentView.addSubview(copyButton) + nameLabel.snp.makeConstraints { (make) in make.left.top.equalToSuperview().offset(15) } @@ -37,21 +40,34 @@ class SoundCell: UITableViewCell { make.top.equalTo(nameLabel.snp.bottom).offset(5) make.bottom.equalToSuperview().offset(-15) } - self.contentView.addSubview(copyButton) copyButton.snp.makeConstraints { (make) in make.right.equalToSuperview().offset(-15) make.centerY.equalToSuperview() make.width.height.equalTo(40) } - copyButton.addTarget(self, action: #selector(copyName), for: .touchUpInside) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - @objc func copyName(){ - if let urlStr = self.nameLabel.text{ - UIPasteboard.general.string = urlStr.trimmingCharacters(in: .whitespacesAndNewlines) - Client.shared.currentNavigationController?.showSnackbar(text: NSLocalizedString("Copy")) + + var viewModel:ViewModel? + func bindViewModel(model:ViewModel){ + guard let viewModel = model as? SoundCellViewModel else { + return } + self.viewModel = model + + viewModel.name + .bind(to: nameLabel.rx.text) + .disposed(by: rx.reuseBag) + viewModel.duration + .map { String(format: "%.2f", CMTimeGetSeconds($0) ) } + .bind(to: durationLabel.rx.text) + .disposed(by: rx.reuseBag) + + copyButton.rx.tap + .map{ viewModel.name.value } + .bind(to: viewModel.copyNameAction) + .disposed(by: rx.reuseBag) } } diff --git a/View/SoundCellViewModel.swift b/View/SoundCellViewModel.swift new file mode 100644 index 0000000..0f39210 --- /dev/null +++ b/View/SoundCellViewModel.swift @@ -0,0 +1,27 @@ +// +// SoundCellViewModel.swift +// Bark +// +// Created by huangfeng on 2020/11/17. +// Copyright © 2020 Fin. All rights reserved. +// + +import Foundation +import RxSwift +import RxCocoa +import AVKit + +class SoundCellViewModel:ViewModel { + let name = BehaviorRelay(value: "") + let duration = BehaviorRelay(value: .zero) + + let copyNameAction = PublishRelay() + let playAction = PublishRelay() + + let model: AVURLAsset + init(model: AVURLAsset) { + self.model = model + name.accept(model.url.deletingPathExtension().lastPathComponent) + duration.accept(model.duration) + } +}