替换为MVVM

This commit is contained in:
Fin 2020-11-18 15:25:44 +08:00
parent 2b1b087047
commit b1e2f527e9
14 changed files with 313 additions and 54 deletions

View File

@ -63,6 +63,10 @@
0637FA8C20E0D7A700E80174 /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0637FA8B20E0D7A700E80174 /* BaseViewController.swift */; }; 0637FA8C20E0D7A700E80174 /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0637FA8B20E0D7A700E80174 /* BaseViewController.swift */; };
063C499520E36BF9001BCA35 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 063C499720E36BF9001BCA35 /* Localizable.strings */; }; 063C499520E36BF9001BCA35 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 063C499720E36BF9001BCA35 /* Localizable.strings */; };
065A4D4220EE1A31002EB2DB /* 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 */; }; 0661A543204FDA4100965E4E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0661A542204FDA4100965E4E /* AppDelegate.swift */; };
0661A545204FDA4100965E4E /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0661A544204FDA4100965E4E /* HomeViewController.swift */; }; 0661A545204FDA4100965E4E /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0661A544204FDA4100965E4E /* HomeViewController.swift */; };
0661A54A204FDA4100965E4E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0661A549204FDA4100965E4E /* Assets.xcassets */; }; 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 = "<group>"; }; 0637FA8B20E0D7A700E80174 /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = "<group>"; };
063C499620E36BF9001BCA35 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; }; 063C499620E36BF9001BCA35 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
063C499820E36C15001BCA35 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; }; 063C499820E36C15001BCA35 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
065BE43F2563D649002A8CA4 /* SoundsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundsViewModel.swift; sourceTree = "<group>"; };
065BE4452563D7E5002A8CA4 /* ViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModelType.swift; sourceTree = "<group>"; };
065BE44A2563D8E1002A8CA4 /* Reusable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reusable.swift; sourceTree = "<group>"; };
065BE44F2563D939002A8CA4 /* SoundCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundCellViewModel.swift; sourceTree = "<group>"; };
0661A53F204FDA4100965E4E /* Bark.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Bark.app; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = "<group>"; }; 0661A542204FDA4100965E4E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
0661A544204FDA4100965E4E /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = "<group>"; }; 0661A544204FDA4100965E4E /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = "<group>"; };
@ -249,6 +257,7 @@
068F66B2247BD84C00DAD25A /* MessageListViewController.swift */, 068F66B2247BD84C00DAD25A /* MessageListViewController.swift */,
06885EB5247FB9880004A303 /* MessageSettingsViewController.swift */, 06885EB5247FB9880004A303 /* MessageSettingsViewController.swift */,
060481ED250F404500BC9799 /* SoundsViewController.swift */, 060481ED250F404500BC9799 /* SoundsViewController.swift */,
065BE43F2563D649002A8CA4 /* SoundsViewModel.swift */,
); );
path = Controller; path = Controller;
sourceTree = "<group>"; sourceTree = "<group>";
@ -263,6 +272,7 @@
06C5953024811392006B98F3 /* ArchiveSettingCell.swift */, 06C5953024811392006B98F3 /* ArchiveSettingCell.swift */,
06C595352481160F006B98F3 /* BKLabel.swift */, 06C595352481160F006B98F3 /* BKLabel.swift */,
060481EF250F51CA00BC9799 /* SoundCell.swift */, 060481EF250F51CA00BC9799 /* SoundCell.swift */,
065BE44F2563D939002A8CA4 /* SoundCellViewModel.swift */,
062B98C2251B2762004562E7 /* BKButton.swift */, 062B98C2251B2762004562E7 /* BKButton.swift */,
062B98C7251B27AE004562E7 /* UINavigationItem+Extension.swift */, 062B98C7251B27AE004562E7 /* UINavigationItem+Extension.swift */,
); );
@ -336,10 +346,20 @@
path = NotificationContentExtension; path = NotificationContentExtension;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
065BE4442563D7AA002A8CA4 /* Common */ = {
isa = PBXGroup;
children = (
065BE4452563D7E5002A8CA4 /* ViewModelType.swift */,
065BE44A2563D8E1002A8CA4 /* Reusable.swift */,
);
path = Common;
sourceTree = "<group>";
};
0661A536204FDA4100965E4E = { 0661A536204FDA4100965E4E = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
063204EF250B6DC2001561EC /* Sounds */, 063204EF250B6DC2001561EC /* Sounds */,
065BE4442563D7AA002A8CA4 /* Common */,
0604F7DD20620D3800B32F09 /* Model */, 0604F7DD20620D3800B32F09 /* Model */,
0604F7DC20620D3400B32F09 /* View */, 0604F7DC20620D3400B32F09 /* View */,
0604F7DB20620D2700B32F09 /* Controller */, 0604F7DB20620D2700B32F09 /* Controller */,
@ -664,12 +684,14 @@
060481EE250F404500BC9799 /* SoundsViewController.swift in Sources */, 060481EE250F404500BC9799 /* SoundsViewController.swift in Sources */,
0603706D20E23EC000F4CA05 /* BarkSFSafariViewController.swift in Sources */, 0603706D20E23EC000F4CA05 /* BarkSFSafariViewController.swift in Sources */,
06C5953124811392006B98F3 /* ArchiveSettingCell.swift in Sources */, 06C5953124811392006B98F3 /* ArchiveSettingCell.swift in Sources */,
065BE4502563D939002A8CA4 /* SoundCellViewModel.swift in Sources */,
06B1158F247BB1FB006D91FB /* Message.swift in Sources */, 06B1158F247BB1FB006D91FB /* Message.swift in Sources */,
06C595362481160F006B98F3 /* BKLabel.swift in Sources */, 06C595362481160F006B98F3 /* BKLabel.swift in Sources */,
0637FA7820E0926D00E80174 /* BarkTargetType.swift in Sources */, 0637FA7820E0926D00E80174 /* BarkTargetType.swift in Sources */,
0637FA7E20E0969800E80174 /* Client.swift in Sources */, 0637FA7E20E0969800E80174 /* Client.swift in Sources */,
0661A545204FDA4100965E4E /* HomeViewController.swift in Sources */, 0661A545204FDA4100965E4E /* HomeViewController.swift in Sources */,
06C5952F248107F5006B98F3 /* iCloudStatusCell.swift in Sources */, 06C5952F248107F5006B98F3 /* iCloudStatusCell.swift in Sources */,
065BE44B2563D8E1002A8CA4 /* Reusable.swift in Sources */,
0637FA8620E0AB6600E80174 /* UIColor+Extension.swift in Sources */, 0637FA8620E0AB6600E80174 /* UIColor+Extension.swift in Sources */,
0637FA8A20E0D58800E80174 /* NewServerViewController.swift in Sources */, 0637FA8A20E0D58800E80174 /* NewServerViewController.swift in Sources */,
0637FA8220E09C4B00E80174 /* BarkNavigationController.swift in Sources */, 0637FA8220E09C4B00E80174 /* BarkNavigationController.swift in Sources */,
@ -685,6 +707,8 @@
06C5952D2480E3F8006B98F3 /* LabelCell.swift in Sources */, 06C5952D2480E3F8006B98F3 /* LabelCell.swift in Sources */,
0637FA7C20E0930E00E80174 /* BarkApi.swift in Sources */, 0637FA7C20E0930E00E80174 /* BarkApi.swift in Sources */,
0637FA8020E0981E00E80174 /* BarkSettings.swift in Sources */, 0637FA8020E0981E00E80174 /* BarkSettings.swift in Sources */,
065BE4402563D649002A8CA4 /* SoundsViewModel.swift in Sources */,
065BE4462563D7E5002A8CA4 /* ViewModelType.swift in Sources */,
0603706B20E20A7C00F4CA05 /* String+Extension.swift in Sources */, 0603706B20E20A7C00F4CA05 /* String+Extension.swift in Sources */,
068F66B3247BD84C00DAD25A /* MessageListViewController.swift in Sources */, 068F66B3247BD84C00DAD25A /* MessageListViewController.swift in Sources */,
0661A543204FDA4100965E4E /* AppDelegate.swift in Sources */, 0661A543204FDA4100965E4E /* AppDelegate.swift in Sources */,

49
Common/Reusable.swift Normal file
View File

@ -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<Void> {
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
}
}

View File

@ -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{ }

View File

@ -9,15 +9,35 @@
import UIKit import UIKit
import Material import Material
class BaseViewController: UIViewController { class BaseViewController: UIViewController {
override func viewDidLoad() { let viewModel:ViewModel
super.viewDidLoad() init(viewModel:ViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
self.view.backgroundColor = Color.grey.lighten5 self.view.backgroundColor = Color.grey.lighten5
} }
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var preferredStatusBarStyle: UIStatusBarStyle{ override var preferredStatusBarStyle: UIStatusBarStyle{
get { get {
return .lightContent return .lightContent
} }
} }
override func viewDidLoad() {
super.viewDidLoad()
makeUI()
bindViewModel()
}
func makeUI() {
}
func bindViewModel(){
}
} }

View File

@ -9,7 +9,7 @@
import UIKit import UIKit
import UserNotifications import UserNotifications
import Material import Material
class HomeViewController: BaseViewController { class HomeViewController: UIViewController {
let newButton: BKButton = { let newButton: BKButton = {
let btn = BKButton() let btn = BKButton()
@ -42,7 +42,7 @@ class HomeViewController: BaseViewController {
notice: NSLocalizedString("setSounds"), notice: NSLocalizedString("setSounds"),
queryParameter: "sound=minuet", queryParameter: "sound=minuet",
moreInfo:NSLocalizedString("viewAllSounds"), moreInfo:NSLocalizedString("viewAllSounds"),
moreViewController: SoundsViewController() moreViewController: SoundsViewController(viewModel: SoundsViewModel())
), ),
PreviewModel( PreviewModel(
body: NSLocalizedString("archiveNotificationMessageTitle"), body: NSLocalizedString("archiveNotificationMessageTitle"),

View File

@ -10,7 +10,7 @@ import UIKit
import Material import Material
import RealmSwift import RealmSwift
class MessageListViewController: BaseViewController { class MessageListViewController: UIViewController {
let tableView: UITableView = { let tableView: UITableView = {
let tableView = UITableView() let tableView = UITableView()
tableView.separatorStyle = .none tableView.separatorStyle = .none

View File

@ -8,7 +8,7 @@
import UIKit import UIKit
import Material import Material
class MessageSettingsViewController: BaseViewController { class MessageSettingsViewController: UIViewController {
let tableView: UITableView = { let tableView: UITableView = {
let tableView = UITableView() let tableView = UITableView()
tableView.separatorStyle = .none tableView.separatorStyle = .none

View File

@ -10,7 +10,7 @@ import UIKit
import Material import Material
import SnapKit import SnapKit
import SafariServices import SafariServices
class NewServerViewController: BaseViewController { class NewServerViewController: UIViewController {
let addressTextField : TextField = { let addressTextField : TextField = {
let textField = TextField() let textField = TextField()

View File

@ -10,55 +10,70 @@ import UIKit
import Material import Material
import AVKit 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 = {
let tableView = UITableView() let tableView = UITableView()
tableView.backgroundColor = Color.grey.lighten5 tableView.backgroundColor = Color.grey.lighten5
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "\(SoundCell.self)") tableView.register(SoundCell.self, forCellReuseIdentifier: "\(SoundCell.self)")
return tableView return tableView
}() }()
let audios:[AVURLAsset] = {
var urls = Bundle.main.urls(forResourcesWithExtension: "caf", subdirectory: nil) ?? [] override func makeUI() {
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()
self.title = NSLocalizedString("notificationSound") self.title = NSLocalizedString("notificationSound")
self.tableView.dataSource = self
self.tableView.delegate = self
self.view.addSubview(self.tableView) self.view.addSubview(self.tableView)
self.tableView.snp.makeConstraints { (make) in self.tableView.snp.makeConstraints { (make) in
make.edges.equalToSuperview() make.edges.equalToSuperview()
} }
let header = UILabel() self.tableView.tableHeaderView = {
header.fontSize = 12 let header = UILabel()
header.text = " \(NSLocalizedString("previewSound"))" header.fontSize = 12
header.textColor = Color.darkText.secondary header.text = " \(NSLocalizedString("previewSound"))"
header.frame = CGRect(x: 0, y: 0, width: 0, height: 40) header.textColor = Color.darkText.secondary
self.tableView.tableHeaderView = header header.frame = CGRect(x: 0, y: 0, width: 0, height: 40)
return header
}()
} }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { override func bindViewModel() {
return audios.count guard let viewModel = viewModel as? SoundsViewModel else {
} return
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)
} }
let output = viewModel.transform(
input: SoundsViewModel.Input(soundSelected: self.tableView.rx
.modelSelected(SoundCellViewModel.self)
.asDriver()))
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String,SoundCellViewModel>> { (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)
} }
} }

View File

@ -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<SoundCellViewModel>
}
struct Output {
var audios:Observable<[SoundCellViewModel]>
var copyNameAction: Driver<String>
var playAction: Driver<CFURL>
}
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 }
)
}
}

View File

@ -15,6 +15,13 @@ def pods
pod 'DeviceKit' pod 'DeviceKit'
pod 'DefaultsKit', :git => 'https://github.com/nmdias/DefaultsKit' pod 'DefaultsKit', :git => 'https://github.com/nmdias/DefaultsKit'
pod 'IceCream' pod 'IceCream'
pod 'RxSwift'
pod 'RxCocoa'
pod 'RxGesture'
pod 'RxDataSources'
pod 'NSObject+Rx'
end end
target 'Bark' do target 'Bark' do

View File

@ -2,6 +2,7 @@ PODS:
- Alamofire (5.2.0) - Alamofire (5.2.0)
- DefaultsKit (0.2.0) - DefaultsKit (0.2.0)
- DeviceKit (3.2.0) - DeviceKit (3.2.0)
- Differentiator (4.0.1)
- FDFullscreenPopGesture (1.1) - FDFullscreenPopGesture (1.1)
- IceCream (1.13.1): - IceCream (1.13.1):
- RealmSwift - RealmSwift
@ -18,12 +19,26 @@ PODS:
- Moya/RxSwift (14.0.0): - Moya/RxSwift (14.0.0):
- Moya/Core - Moya/Core
- RxSwift (~> 5.0) - RxSwift (~> 5.0)
- "NSObject+Rx (5.1.0)":
- RxSwift (~> 5.1)
- ObjectMapper (4.2.0) - ObjectMapper (4.2.0)
- Realm (5.0.0): - Realm (5.0.0):
- Realm/Headers (= 5.0.0) - Realm/Headers (= 5.0.0)
- Realm/Headers (5.0.0) - Realm/Headers (5.0.0)
- RealmSwift (5.0.0): - RealmSwift (5.0.0):
- Realm (= 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) - RxSwift (5.1.1)
- SnapKit (5.0.1) - SnapKit (5.0.1)
- SVProgressHUD (2.2.5) - SVProgressHUD (2.2.5)
@ -37,12 +52,24 @@ DEPENDENCIES:
- KVOController - KVOController
- Material - Material
- Moya/RxSwift - Moya/RxSwift
- "NSObject+Rx"
- ObjectMapper - ObjectMapper
- RxCocoa
- RxDataSources
- RxGesture
- RxSwift
- SnapKit - SnapKit
- SVProgressHUD - SVProgressHUD
- SwiftyJSON - SwiftyJSON
SPEC REPOS: SPEC REPOS:
https://github.com/CocoaPods/Specs.git:
- Differentiator
- "NSObject+Rx"
- RxCocoa
- RxDataSources
- RxGesture
- RxRelay
trunk: trunk:
- Alamofire - Alamofire
- DeviceKit - DeviceKit
@ -73,20 +100,26 @@ SPEC CHECKSUMS:
Alamofire: c1ca147559e730bfb2182c8c7aafbdd90a867987 Alamofire: c1ca147559e730bfb2182c8c7aafbdd90a867987
DefaultsKit: 6c767941b2c3fe34c4d70e300e6c3b80c94964dd DefaultsKit: 6c767941b2c3fe34c4d70e300e6c3b80c94964dd
DeviceKit: d081659419cce07c0b5239dbc9fb39ed7413c7fe DeviceKit: d081659419cce07c0b5239dbc9fb39ed7413c7fe
Differentiator: 886080237d9f87f322641dedbc5be257061b0602
FDFullscreenPopGesture: a8a620179e3d9c40e8e00256dcee1c1a27c6d0f0 FDFullscreenPopGesture: a8a620179e3d9c40e8e00256dcee1c1a27c6d0f0
IceCream: 0447d87b55df85651dd60b15712cef64dbe1cb54 IceCream: 0447d87b55df85651dd60b15712cef64dbe1cb54
KVOController: d72ace34afea42468329623b3379ab3cd1d286b6 KVOController: d72ace34afea42468329623b3379ab3cd1d286b6
Material: a2a3f400a3b549d53ef89e56c58c4535b29db387 Material: a2a3f400a3b549d53ef89e56c58c4535b29db387
Motion: cf1e060e489f6661126374d5c60dbd2ed991605c Motion: cf1e060e489f6661126374d5c60dbd2ed991605c
Moya: 5b45dacb75adb009f97fde91c204c1e565d31916 Moya: 5b45dacb75adb009f97fde91c204c1e565d31916
"NSObject+Rx": fa6bbcc1ab1faa06b01466bc09b1e0692bbc5946
ObjectMapper: 1eb41f610210777375fa806bf161dc39fb832b81 ObjectMapper: 1eb41f610210777375fa806bf161dc39fb832b81
Realm: 76066d333de26f97e9caedaa85ee3ff4a62ca265 Realm: 76066d333de26f97e9caedaa85ee3ff4a62ca265
RealmSwift: c1bdff09b422569dc6881410d9ccf712bcdb79b0 RealmSwift: c1bdff09b422569dc6881410d9ccf712bcdb79b0
RxCocoa: 32065309a38d29b5b0db858819b5bf9ef038b601
RxDataSources: efee07fa4de48477eca0a4611e6d11e2da9c1114
RxGesture: d6bd7447ca3a596c7a9702a6a2b6a2bb5d8bae54
RxRelay: d77f7d771495f43c556cbc43eebd1bb54d01e8e9
RxSwift: 81470a2074fa8780320ea5fe4102807cb7118178 RxSwift: 81470a2074fa8780320ea5fe4102807cb7118178
SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb
SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6 SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
SwiftyJSON: 36413e04c44ee145039d332b4f4e2d3e8d6c4db7 SwiftyJSON: 36413e04c44ee145039d332b4f4e2d3e8d6c4db7
PODFILE CHECKSUM: 90c7dcd577fc40757291126890d6791408bb905a PODFILE CHECKSUM: f0191b272c872b4d43da19a114ad4be556fb7eba
COCOAPODS: 1.8.4 COCOAPODS: 1.10.0

View File

@ -8,6 +8,7 @@
import UIKit import UIKit
import Material import Material
import AVKit
class SoundCell: UITableViewCell { class SoundCell: UITableViewCell {
let copyButton = IconButton(image: UIImage(named: "baseline_file_copy_white_24pt"), tintColor: Color.grey.base) 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(nameLabel)
self.contentView.addSubview(durationLabel) self.contentView.addSubview(durationLabel)
self.contentView.addSubview(copyButton)
nameLabel.snp.makeConstraints { (make) in nameLabel.snp.makeConstraints { (make) in
make.left.top.equalToSuperview().offset(15) make.left.top.equalToSuperview().offset(15)
} }
@ -37,21 +40,34 @@ class SoundCell: UITableViewCell {
make.top.equalTo(nameLabel.snp.bottom).offset(5) make.top.equalTo(nameLabel.snp.bottom).offset(5)
make.bottom.equalToSuperview().offset(-15) make.bottom.equalToSuperview().offset(-15)
} }
self.contentView.addSubview(copyButton)
copyButton.snp.makeConstraints { (make) in copyButton.snp.makeConstraints { (make) in
make.right.equalToSuperview().offset(-15) make.right.equalToSuperview().offset(-15)
make.centerY.equalToSuperview() make.centerY.equalToSuperview()
make.width.height.equalTo(40) make.width.height.equalTo(40)
} }
copyButton.addTarget(self, action: #selector(copyName), for: .touchUpInside)
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@objc func copyName(){
if let urlStr = self.nameLabel.text{ var viewModel:ViewModel?
UIPasteboard.general.string = urlStr.trimmingCharacters(in: .whitespacesAndNewlines) func bindViewModel(model:ViewModel){
Client.shared.currentNavigationController?.showSnackbar(text: NSLocalizedString("Copy")) 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)
} }
} }

View File

@ -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<String>(value: "")
let duration = BehaviorRelay<CMTime>(value: .zero)
let copyNameAction = PublishRelay<String>()
let playAction = PublishRelay<CFURL>()
let model: AVURLAsset
init(model: AVURLAsset) {
self.model = model
name.accept(model.url.deletingPathExtension().lastPathComponent)
duration.accept(model.duration)
}
}