diff --git a/Bark.xcodeproj/project.pbxproj b/Bark.xcodeproj/project.pbxproj index 948c059..e42b3cc 100644 --- a/Bark.xcodeproj/project.pbxproj +++ b/Bark.xcodeproj/project.pbxproj @@ -184,6 +184,9 @@ 06F11E7727D9D5FB00F00298 /* QRScannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F11E7627D9D5FB00F00298 /* QRScannerViewController.swift */; }; 06FB04042C53575400F3A213 /* SharedDefines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FB04032C53575400F3A213 /* SharedDefines.swift */; }; 06FB04052C53575400F3A213 /* SharedDefines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FB04032C53575400F3A213 /* SharedDefines.swift */; }; + 1E73F99E2C282822002BF649 /* SectionViewController-iPad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E73F99D2C282822002BF649 /* SectionViewController-iPad.swift */; }; + 1EFB545D2C314A6800B8E51B /* BarkSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EFB545C2C314A6800B8E51B /* BarkSplitViewController.swift */; }; + 1EFB545F2C32514000B8E51B /* SectionViewModel-iPad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EFB545E2C32514000B8E51B /* SectionViewModel-iPad.swift */; }; 3428272069AFAFE2C683FEB0 /* libPods-Bark.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CCC722470308049D180876C7 /* libPods-Bark.a */; }; 879AE4D4178855A9672009E4 /* libPods-NotificationServiceExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B7F8BDFAA047451561798F58 /* libPods-NotificationServiceExtension.a */; }; B963F7D5BA7AC2571E71EF66 /* libPods-BarkTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 76381A752CCCD4DA6BB2A566 /* libPods-BarkTests.a */; }; @@ -375,6 +378,9 @@ 06FB04032C53575400F3A213 /* SharedDefines.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedDefines.swift; sourceTree = ""; }; 121D9B1ED4E8D26F345BC5C0 /* Pods-BarkTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BarkTests.release.xcconfig"; path = "Target Support Files/Pods-BarkTests/Pods-BarkTests.release.xcconfig"; sourceTree = ""; }; 138CE8CB688587E893BC5C44 /* Pods-Bark.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Bark.debug.xcconfig"; path = "Target Support Files/Pods-Bark/Pods-Bark.debug.xcconfig"; sourceTree = ""; }; + 1E73F99D2C282822002BF649 /* SectionViewController-iPad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SectionViewController-iPad.swift"; sourceTree = ""; }; + 1EFB545C2C314A6800B8E51B /* BarkSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarkSplitViewController.swift; sourceTree = ""; }; + 1EFB545E2C32514000B8E51B /* SectionViewModel-iPad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SectionViewModel-iPad.swift"; sourceTree = ""; }; 519481D715B40109627E1B49 /* Pods-NotificationServiceExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationServiceExtension.release.xcconfig"; path = "Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.release.xcconfig"; sourceTree = ""; }; 76381A752CCCD4DA6BB2A566 /* libPods-BarkTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-BarkTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; A69B47DA6DB3B168D5770B45 /* Pods-Bark.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Bark.release.xcconfig"; path = "Target Support Files/Pods-Bark/Pods-Bark.release.xcconfig"; sourceTree = ""; }; @@ -447,6 +453,9 @@ 06EEF332291CCFF400CA228A /* CryptoSettingController.swift */, 06EEF334291CD00000CA228A /* CryptoSettingViewModel.swift */, 06787C3A2AB82BDB008ABDD7 /* CrashReportViewController.swift */, + 1E73F99D2C282822002BF649 /* SectionViewController-iPad.swift */, + 1EFB545E2C32514000B8E51B /* SectionViewModel-iPad.swift */, + 1EFB545C2C314A6800B8E51B /* BarkSplitViewController.swift */, ); path = Controller; sourceTree = ""; @@ -1098,6 +1107,7 @@ 06840DBB272298FB001B3193 /* BKColor.swift in Sources */, 06C2CF232685B88D0034B127 /* TextCell.swift in Sources */, 06BBB896256518760076F63E /* NewServerViewModel.swift in Sources */, + 1EFB545D2C314A6800B8E51B /* BarkSplitViewController.swift in Sources */, 06BBB8C92567B6730076F63E /* Operators.swift in Sources */, 0603706920E1F89500F4CA05 /* PreviewCardCell.swift in Sources */, 0627DABB298B6EA2002F3F69 /* DropBoxView.swift in Sources */, @@ -1161,12 +1171,14 @@ 06C2CF252685BDB80034B127 /* SpacerCell.swift in Sources */, 06BBB8BC2567B3AD0076F63E /* ArchiveSettingCellViewModel.swift in Sources */, 0642B55A27EB13F100453D91 /* MutableTextCell.swift in Sources */, + 1EFB545F2C32514000B8E51B /* SectionViewModel-iPad.swift in Sources */, 06EEF335291CD00000CA228A /* CryptoSettingViewModel.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 */, + 1E73F99E2C282822002BF649 /* SectionViewController-iPad.swift in Sources */, 06BBB8B72567AC140076F63E /* MessageSettingsViewModel.swift in Sources */, 06BBB8C12567B3EF0076F63E /* BaseTableViewCell.swift in Sources */, 0661A543204FDA4100965E4E /* AppDelegate.swift in Sources */, @@ -1433,8 +1445,10 @@ PRODUCT_BUNDLE_IDENTIFIER = me.fin.bark; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match Development me.fin.bark"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -1455,8 +1469,10 @@ PRODUCT_BUNDLE_IDENTIFIER = me.fin.bark; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "match AppStore me.fin.bark"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; diff --git a/Bark/AppDelegate.swift b/Bark/AppDelegate.swift index 9d5fcd9..d4bc498 100644 --- a/Bark/AppDelegate.swift +++ b/Bark/AppDelegate.swift @@ -78,25 +78,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD setupRealm() IQKeyboardManager.shared.enable = true - - let tabBarController = StateStorageTabBarController() - tabBarController.tabBar.tintColor = BKColor.grey.darken4 - - self.window?.rootViewController = BarkSnackbarController( - rootViewController: tabBarController - ) - - tabBarController.viewControllers = [ - BarkNavigationController(rootViewController: HomeViewController(viewModel: HomeViewModel())), - BarkNavigationController(rootViewController: MessageListViewController(viewModel: MessageListViewModel())), - BarkNavigationController(rootViewController: MessageSettingsViewController(viewModel: MessageSettingsViewModel())) - ] - - let tabBarItems = [UITabBarItem(title: NSLocalizedString("service"), image: UIImage(named: "baseline_gite_black_24pt"), tag: 0), - UITabBarItem(title: NSLocalizedString("historyMessage"), image: Icon.history, tag: 1), - UITabBarItem(title: NSLocalizedString("settings"), image: UIImage(named: "baseline_manage_accounts_black_24pt"), tag: 2)] - for (index, viewController) in tabBarController.viewControllers!.enumerated() { - viewController.tabBarItem = tabBarItems[index] + if #available(iOS 14, *), UIDevice.current.userInterfaceIdiom == .pad { + let splitViewController = BarkSplitViewController.init(style: .doubleColumn) + splitViewController.initViewControllers() + self.window?.rootViewController = splitViewController; + } else { + let tabBarController = StateStorageTabBarController() + tabBarController.tabBar.tintColor = BKColor.grey.darken4 + + self.window?.rootViewController = BarkSnackbarController( + rootViewController: tabBarController + ) + + tabBarController.viewControllers = [ + BarkNavigationController(rootViewController: HomeViewController(viewModel: HomeViewModel())), + BarkNavigationController(rootViewController: MessageListViewController(viewModel: MessageListViewModel())), + BarkNavigationController(rootViewController: MessageSettingsViewController(viewModel: MessageSettingsViewModel())) + ] + + let tabBarItems = [UITabBarItem(title: NSLocalizedString("service"), image: UIImage(named: "baseline_gite_black_24pt"), tag: 0), + UITabBarItem(title: NSLocalizedString("historyMessage"), image: Icon.history, tag: 1), + UITabBarItem(title: NSLocalizedString("settings"), image: UIImage(named: "baseline_manage_accounts_black_24pt"), tag: 2)] + for (index, viewController) in tabBarController.viewControllers!.enumerated() { + viewController.tabBarItem = tabBarItems[index] + } } // 需先配置好 tabBarController 的 viewControllers,显示时会默认显示上次打开的页面 diff --git a/Controller/BarkSplitViewController.swift b/Controller/BarkSplitViewController.swift new file mode 100644 index 0000000..4ce62b7 --- /dev/null +++ b/Controller/BarkSplitViewController.swift @@ -0,0 +1,68 @@ +// +// BarkSplitViewController.swift +// Bark +// +// Created by sidguan on 2024/6/30. +// Copyright © 2024 Fin. All rights reserved. +// + +import UIKit +import Material + +class BarkSplitViewController: UISplitViewController { + + override func viewDidLoad() { + super.viewDidLoad() +// self.displayModeButtonItem.tintColor = BKColor.grey.darken4 +// self.delegate = self + // Do any additional setup after loading the view. + } + + func initViewControllers() { + if #available(iOS 14, *) { + let sectionViewController = BarkNavigationController(rootViewController: SectionViewController_iPad(viewModel: SectionViewModel())); + let homeViewController = BarkNavigationController(rootViewController: HomeViewController(viewModel: HomeViewModel())); + let tabBarController = StateStorageTabBarController() + tabBarController.tabBar.tintColor = BKColor.grey.darken4 + + let snackBarController = BarkSnackbarController( + rootViewController: tabBarController + ) + + tabBarController.viewControllers = [ + BarkNavigationController(rootViewController: HomeViewController(viewModel: HomeViewModel())), + BarkNavigationController(rootViewController: MessageListViewController(viewModel: MessageListViewModel())), + BarkNavigationController(rootViewController: MessageSettingsViewController(viewModel: MessageSettingsViewModel())) + ] + + let tabBarItems = [ + UITabBarItem(title: NSLocalizedString("service"), image: UIImage(named: "baseline_gite_black_24pt"), tag: 0), + UITabBarItem(title: NSLocalizedString("historyMessage"), image: Icon.history, tag: 1), + UITabBarItem(title: NSLocalizedString("settings"), image: UIImage(named: "baseline_manage_accounts_black_24pt"), tag: 2) + ] + for (index, viewController) in tabBarController.viewControllers!.enumerated() { + viewController.tabBarItem = tabBarItems[index] + } + + + self.setViewController(sectionViewController, for: .primary) + self.setViewController(homeViewController, for: .secondary) + self.setViewController(snackBarController, for: .compact) + } + } + +// func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool { +// return true +// } + + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + // Get the new view controller using segue.destination. + // Pass the selected object to the new view controller. + } + */ + +} diff --git a/Controller/BaseViewController.swift b/Controller/BaseViewController.swift index 8068d61..74ef5a7 100644 --- a/Controller/BaseViewController.swift +++ b/Controller/BaseViewController.swift @@ -28,7 +28,11 @@ class BaseViewController: UIViewController where T: ViewModel { override func viewDidLoad() { super.viewDidLoad() - self.navigationItem.largeTitleDisplayMode = .automatic + if UIDevice.current.userInterfaceIdiom == .pad { + navigationItem.largeTitleDisplayMode = .never + } else { + navigationItem.largeTitleDisplayMode = .automatic + } makeUI() } diff --git a/Controller/MessageListViewController.swift b/Controller/MessageListViewController.swift index 183c0d9..dd107bd 100644 --- a/Controller/MessageListViewController.swift +++ b/Controller/MessageListViewController.swift @@ -31,18 +31,18 @@ enum MessageDeleteType: Int { } class MessageListViewController: BaseViewController { - let deleteButton: BKButton = { + let deleteButton: UIBarButtonItem = { let btn = BKButton() btn.setImage(UIImage(named: "baseline_delete_outline_black_24pt"), for: .normal) btn.frame = CGRect(x: 0, y: 0, width: 40, height: 40) - return btn + return UIBarButtonItem(customView: btn) }() - let groupButton: BKButton = { + let groupButton: UIBarButtonItem = { let btn = BKButton() btn.setImage(UIImage(named: "baseline_folder_open_black_24pt"), for: .normal) btn.frame = CGRect(x: 0, y: 0, width: 40, height: 40) - return btn + return UIBarButtonItem(customView: btn) }() let tableView: UITableView = { @@ -59,7 +59,7 @@ class MessageListViewController: BaseViewController { navigationItem.searchController?.obscuresBackgroundDuringPresentation = false navigationItem.searchController?.delegate = self - navigationItem.setBarButtonItems(items: [UIBarButtonItem(customView: deleteButton), UIBarButtonItem(customView: groupButton)], position: .right) + navigationItem.setBarButtonItems(items: [deleteButton, groupButton], position: .right) self.view.addSubview(tableView) tableView.snp.makeConstraints { make in @@ -96,9 +96,12 @@ class MessageListViewController: BaseViewController { } override func bindViewModel() { - let batchDelete = deleteButton.rx + guard let deleteBtn = deleteButton.customView as? BKButton else { + return; + } + let batchDelete = deleteBtn.rx .tap - .flatMapLatest { _ -> PublishRelay in + .flatMapLatest {_ -> PublishRelay in let relay = PublishRelay() func alert(_ type: MessageDeleteType) { @@ -124,19 +127,31 @@ class MessageListViewController: BaseViewController { alert(.allTime) })) alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil)) + if UIDevice.current.userInterfaceIdiom == .pad { + alertController.modalPresentationStyle = .popover + if #available(iOS 16.0, *) { + alertController.popoverPresentationController?.sourceItem = self.deleteButton + } else { + alertController.popoverPresentationController?.barButtonItem = self.deleteButton + } + } self.navigationController?.present(alertController, animated: true, completion: nil) return relay } + guard let groupBtn = groupButton.customView as? BKButton else { + return; + } + let output = viewModel.transform( input: MessageListViewModel.Input( refresh: tableView.refreshControl!.rx.controlEvent(.valueChanged).asDriver(), loadMore: tableView.mj_footer!.rx.refresh.asDriver(), itemDelete: tableView.rx.itemDeleted.asDriver(), - itemSelected: tableView.rx.modelSelected(MessageTableViewCellViewModel.self).asDriver(), + itemSelected: tableView.rx.itemSelected.asDriver(), delete: batchDelete.asDriver(onErrorDriveWith: .empty()), - groupTap: groupButton.rx.tap.asDriver(), + groupTap: groupBtn.rx.tap.asDriver(), searchText: navigationItem.searchController!.searchBar.rx.text.asObservable())) // tableView 刷新状态 @@ -166,7 +181,7 @@ class MessageListViewController: BaseViewController { // message操作alert output.alertMessage.drive(onNext: { [weak self] message in - self?.alertMessage(message: message) + self?.alertMessage(message: message.0, indexPath: message.1) }).disposed(by: rx.disposeBag) // 选择群组 @@ -183,7 +198,7 @@ class MessageListViewController: BaseViewController { self.scrollToTop() } - func alertMessage(message: String) { + func alertMessage(message: String, indexPath: IndexPath) { let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) let copyAction = UIAlertAction(title: NSLocalizedString("CopyAll"), style: .default, handler: { [weak self] (_: UIAlertAction) -> Void in @@ -195,6 +210,13 @@ class MessageListViewController: BaseViewController { alertController.addAction(copyAction) alertController.addAction(cancelAction) + if UIDevice.current.userInterfaceIdiom == .pad { + alertController.modalPresentationStyle = .popover + if let cell = self.tableView.cellForRow(at: indexPath) { + alertController.popoverPresentationController?.sourceView = self.tableView + alertController.popoverPresentationController?.sourceRect = cell.frame + } + } self.navigationController?.present(alertController, animated: true, completion: nil) } diff --git a/Controller/MessageListViewModel.swift b/Controller/MessageListViewModel.swift index 710dd35..faae1c5 100644 --- a/Controller/MessageListViewModel.swift +++ b/Controller/MessageListViewModel.swift @@ -11,13 +11,14 @@ import RealmSwift import RxCocoa import RxDataSources import RxSwift +import UIKit class MessageListViewModel: ViewModel, ViewModelType { struct Input { var refresh: Driver var loadMore: Driver var itemDelete: Driver - var itemSelected: Driver + var itemSelected: Driver var delete: Driver var groupTap: Driver var searchText: Observable @@ -26,7 +27,7 @@ class MessageListViewModel: ViewModel, ViewModelType { struct Output { var messages: Driver<[MessageSection]> var refreshAction: Driver - var alertMessage: Driver + var alertMessage: Driver<(String, IndexPath)> var groupFilter: Driver var title: Driver } @@ -70,8 +71,12 @@ class MessageListViewModel: ViewModel, ViewModelType { } func transform(input: Input) -> Output { - let alertMessage = input.itemSelected.map { model -> String in - let message = model.message + let alertMessage = input.itemSelected.map({ [weak self] indexPath in + guard let results = self?.results else { + return ("", IndexPath(row: 0, section: 0)); + } + let message = results[indexPath.row] +// let message = model.message var copyContent: String = "" if let title = message.title { @@ -85,8 +90,8 @@ class MessageListViewModel: ViewModel, ViewModelType { } copyContent = String(copyContent.prefix(copyContent.count - 1)) - return copyContent - } + return (copyContent, indexPath) + }) // 标题 let titleRelay = BehaviorRelay(value: NSLocalizedString("historyMessage")) // 数据源 diff --git a/Controller/MessageSettingsViewController.swift b/Controller/MessageSettingsViewController.swift index 173136b..3ae1271 100644 --- a/Controller/MessageSettingsViewController.swift +++ b/Controller/MessageSettingsViewController.swift @@ -64,14 +64,18 @@ class MessageSettingsViewController: BaseViewController (Driver, Driver) { let backupOrRestoreAction = self.tableView.rx - .modelSelected(MessageSettingItem.self) - .filter { item in - if case MessageSettingItem.backup = item { +// .modelSelected(MessageSettingItem.self) + .itemSelected + .filter { indexPath in + guard let viewModel: MessageSettingItem = try? self.tableView.rx.model(at: indexPath) else { + return false; + } + if case MessageSettingItem.backup = viewModel { return true } return false } - .flatMapLatest { [weak self] _ in + .flatMapLatest { [weak self] indexPath in guard let strongSelf = self else { return Observable.empty() } @@ -91,7 +95,15 @@ class MessageSettingsViewController: BaseViewController, UITableViewDelegate { + + let tableView: UITableView = { + let tableView = UITableView(frame: .zero, style: .grouped) + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "\(UITableViewCell.self)") + tableView.backgroundColor = BKColor.background.primary + return tableView + }() + + override func viewDidLoad() { + super.viewDidLoad() + self.title = "Bark" + navigationItem.largeTitleDisplayMode = .automatic + } + + override func makeUI() { + self.view.addSubview(tableView) + tableView.delegate = self + tableView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + + override func bindViewModel() { + let output = viewModel.transform(input: SectionViewModel.Input()) + let dataSource = RxTableViewSectionedReloadDataSource> { + _, tableView, _, item -> UITableViewCell in + guard let cell = tableView.dequeueReusableCell(withIdentifier: "\(UITableViewCell.self)") else { + return UITableViewCell() + } + + if #available(iOS 14, *) { + cell.backgroundConfiguration = UIBackgroundConfiguration.listPlainCell() + } + cell.selectionStyle = .gray + cell.imageView?.image = item.image + cell.imageView?.tintColor = BKColor.grey.darken4 + cell.textLabel?.text = item.title + return cell + } + tableView.rx + .itemSelected + .flatMapLatest { indexPath -> Observable in + return Observable.just(indexPath) + } + .subscribe { indexPath in + if #available(iOS 14, *) { + if indexPath.row == 0 { + self.splitViewController?.setViewController( + BarkNavigationController(rootViewController: HomeViewController(viewModel: HomeViewModel())), for: .secondary) + } else if indexPath.row == 1 { + self.splitViewController?.setViewController( + BarkNavigationController(rootViewController: MessageListViewController(viewModel: MessageListViewModel())), for: .secondary) + } else if (indexPath.row == 2) { + + self.splitViewController?.setViewController( + BarkNavigationController(rootViewController: MessageSettingsViewController(viewModel: MessageSettingsViewModel())), for: .secondary) + } + } else { + // 正常应该走不到这里了 + } + + }.disposed(by: rx.disposeBag) + output.items + .bind(to: tableView.rx.items(dataSource: dataSource)) + .disposed(by: rx.disposeBag) + } + + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 55 + } + + /* + // Override to support conditional editing of the table view. + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the specified item to be editable. + return true + } + */ + + /* + // Override to support editing the table view. + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + // Delete the row from the data source + tableView.deleteRows(at: [indexPath], with: .fade) + } else if editingStyle == .insert { + // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view + } + } + */ + + /* + // Override to support rearranging the table view. + override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) { + + } + */ + + /* + // Override to support conditional rearranging of the table view. + override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { + // Return false if you do not want the item to be re-orderable. + return true + } + */ + + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + // Get the new view controller using segue.destination. + // Pass the selected object to the new view controller. + } + */ + +} diff --git a/Controller/SectionViewModel-iPad.swift b/Controller/SectionViewModel-iPad.swift new file mode 100644 index 0000000..f7476d4 --- /dev/null +++ b/Controller/SectionViewModel-iPad.swift @@ -0,0 +1,50 @@ +// +// SectionViewModel-iPad.swift +// Bark +// +// Created by sidguan on 2024/7/1. +// Copyright © 2024 Fin. All rights reserved. +// + +import Foundation +import RxCocoa +import RxDataSources +import RxSwift +import Material + +struct SectionItem { + let image: UIImage? + let title: String +} + +class SectionViewModel: ViewModel, ViewModelType { + struct Input { +// var sectionSelected: Driver + } + + struct Output { + var items: Observable<[SectionModel]> +// var selectedItem: Observable + } + + func initSectionItems() -> Observable<[SectionModel]> { + return Observable.create { (observer) -> Disposable in + let sectionItems = [ + SectionItem(image: UIImage(named: "baseline_gite_black_24pt"), title: NSLocalizedString("service")), + SectionItem(image: Icon.history, title: NSLocalizedString("historyMessage")), + SectionItem(image: UIImage(named: "baseline_manage_accounts_black_24pt"), title: NSLocalizedString("settings")), + ] + let section = [SectionModel(model: "", items: sectionItems)] + observer.onNext(section) + observer.onCompleted() + return Disposables.create() + } + } + + func transform(input: Input) -> Output { + let sectionItems = initSectionItems() + return Output( + items: sectionItems + ) + } +} diff --git a/Controller/ServerListViewController.swift b/Controller/ServerListViewController.swift index 94a56fa..b857a3b 100644 --- a/Controller/ServerListViewController.swift +++ b/Controller/ServerListViewController.swift @@ -124,9 +124,12 @@ class ServerListViewController: BaseViewController { func getServerAction() -> Driver<(Server, ServerActionType)> { return tableView.rx - .modelSelected(ServerListTableViewCellViewModel.self) - .flatMapLatest { viewModel -> PublishRelay<(Server, ServerActionType)> in + .itemSelected + .flatMapLatest { indexPath in let relay = PublishRelay<(Server, ServerActionType)>() + guard let viewModel: ServerListTableViewCellViewModel = try? self.tableView.rx.model(at: indexPath) else { + return relay; + } let alertController = UIAlertController(title: nil, message: "\(viewModel.address.value)", preferredStyle: .actionSheet) alertController.addAction(UIAlertAction(title: NSLocalizedString("copyAddressAndKey"), style: .default, handler: { _ in @@ -157,6 +160,14 @@ class ServerListViewController: BaseViewController { })) alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel"), style: .cancel, handler: nil)) + + if UIDevice.current.userInterfaceIdiom == .pad { + alertController.modalPresentationStyle = .popover + if let cell = self.tableView.cellForRow(at: indexPath) { + alertController.popoverPresentationController?.sourceView = self.tableView + alertController.popoverPresentationController?.sourceRect = cell.frame + } + } self.navigationController?.present(alertController, animated: true, completion: nil) return relay