From 22553b661dbb7e4aa34876a9e2a801015a2d1288 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sat, 7 Sep 2019 14:00:31 -0500 Subject: [PATCH] Begin to convert Settings to the latest SwiftUI --- NetNewsWire.xcodeproj/project.pbxproj | 4 +- iOS/SceneCoordinator.swift | 18 ++- iOS/Settings/SettingsView.swift | 208 +++++++++++++++----------- 3 files changed, 131 insertions(+), 99 deletions(-) diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 289353912..3570e1595 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -59,6 +59,7 @@ 51938DF2231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; }; 51938DF3231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; }; 519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519B8D322143397200FA689C /* SharingServiceDelegate.swift */; }; + 519D73FB2323FF35008BB345 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F35D0822AFD4760003CE1B /* SettingsView.swift */; }; 519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.swift */; }; 51C451A9226377C200C03939 /* ArticlesDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; }; 51C451AA226377C200C03939 /* ArticlesDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -784,8 +785,8 @@ 55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsReaderAPI.xib; sourceTree = ""; }; 55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsReaderAPIWindowController.swift; sourceTree = ""; }; 5F323808231DF9F000706F6B /* NNWTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NNWTableViewCell.swift; sourceTree = ""; }; - 6581C73320CED60000F4AD34 /* Subscribe to Feed.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Subscribe to Feed.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 6543108B2322D90900658221 /* common */ = {isa = PBXFileReference; lastKnownFileType = folder; path = common; sourceTree = ""; }; + 6581C73320CED60000F4AD34 /* Subscribe to Feed.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Subscribe to Feed.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 6581C73420CED60100F4AD34 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 6581C73720CED60100F4AD34 /* SafariExtensionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariExtensionHandler.swift; sourceTree = ""; }; 6581C73920CED60100F4AD34 /* SafariExtensionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariExtensionViewController.swift; sourceTree = ""; }; @@ -2515,6 +2516,7 @@ 51934CD023108953006127BE /* ActivityID.swift in Sources */, 51C452782265091600C03939 /* MasterTimelineCellData.swift in Sources */, 51C45259226508D300C03939 /* AppDefaults.swift in Sources */, + 519D73FB2323FF35008BB345 /* SettingsView.swift in Sources */, 511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */, 51C45293226509C800C03939 /* StarredFeedDelegate.swift in Sources */, 51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */, diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift index cc0cc47f8..e815d9170 100644 --- a/iOS/SceneCoordinator.swift +++ b/iOS/SceneCoordinator.swift @@ -6,7 +6,8 @@ // Copyright © 2019 Ranchero Software. All rights reserved. // -import Foundation +import UIKit +import SwiftUI import Account import Articles import RSCore @@ -852,14 +853,15 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { } func showSettings() { - let settingsNavViewController = UIStoryboard.settings.instantiateInitialViewController() as! UINavigationController - settingsNavViewController.modalPresentationStyle = .formSheet - let settingsViewController = settingsNavViewController.topViewController as! SettingsViewController - settingsViewController.presentingParentController = rootSplitViewController - rootSplitViewController.present(settingsNavViewController, animated: true) +// let settingsNavViewController = UIStoryboard.settings.instantiateInitialViewController() as! UINavigationController +// settingsNavViewController.modalPresentationStyle = .formSheet +// let settingsViewController = settingsNavViewController.topViewController as! SettingsViewController +// settingsViewController.presentingParentController = rootSplitViewController +// rootSplitViewController.present(settingsNavViewController, animated: true) - // let settings = UIHostingController(rootView: SettingsView(viewModel: SettingsView.ViewModel())) - // self.present(settings, animated: true) + let settings = UIHostingController(rootView: SettingsView(viewModel: SettingsView.ViewModel())) + settings.modalPresentationStyle = .formSheet + rootSplitViewController.present(settings, animated: true) } func showAdd(_ type: AddControllerType) { diff --git a/iOS/Settings/SettingsView.swift b/iOS/Settings/SettingsView.swift index bf7eacc1a..005f39e4f 100644 --- a/iOS/Settings/SettingsView.swift +++ b/iOS/Settings/SettingsView.swift @@ -11,26 +11,29 @@ import Combine import Account struct SettingsView : View { - @ObjectBinding var viewModel: ViewModel - @State var subscriptionsImportAccounts: ActionSheet? = nil - @State var subscriptionsImportDocumentPicker: Modal? = nil - @State var subscriptionsExportAccounts: ActionSheet? = nil - @State var subscriptionsExportDocumentPicker: Modal? = nil + + @ObservedObject var viewModel: ViewModel + + @State var isWebsitePresented: Bool = false + @State var isGithubRepoPresented: Bool = false + @State var isBugTrackerPresented: Bool = false + @State var isTechnotesPresented: Bool = false + @State var isHowToSupportPresented: Bool = false var body: some View { NavigationView { Form { - Section(header: Text("ACCOUNTS")) { - ForEach(viewModel.accounts.identified(by: \.self)) { account in - NavigationButton(destination: SettingsDetailAccountView(viewModel: SettingsDetailAccountView.ViewModel(account)), isDetail: false) { - Text(verbatim: account.nameForDisplay) - } - } - NavigationButton(destination: SettingsAddAccountView(), isDetail: false) { - Text("Add Account") - } - } +// Section(header: Text("ACCOUNTS")) { +// ForEach(viewModel.accounts.identified(by: \.self)) { account in +// NavigationLink(destination: SettingsDetailAccountView(viewModel: SettingsDetailAccountView.ViewModel(account)), isDetail: false) { +// Text(verbatim: account.nameForDisplay) +// } +// } +// NavigationLink(destination: SettingsAddAccountView(), isDetail: false) { +// Text("Add Account") +// } +// } Section(header: Text("TIMELINE")) { Toggle(isOn: $viewModel.sortOldestToNewest) { @@ -41,90 +44,115 @@ struct SettingsView : View { } } - Section(header: Text("DATABASE")) { - Picker(selection: $viewModel.refreshInterval, label: Text("Refresh Interval")) { - ForEach(RefreshInterval.allCases.identified(by: \.self)) { interval in - Text(interval.description()).tag(interval) - } - } - Button(action: { - self.subscriptionsImportAccounts = self.createSubscriptionsImportAccounts - }) { - Text("Import Subscriptions...") - } - .presentation(subscriptionsImportAccounts) - .presentation(subscriptionsImportDocumentPicker) - Button(action: { - self.subscriptionsExportAccounts = self.createSubscriptionsExportAccounts - }) { - Text("Export Subscriptions...") - } - .presentation(subscriptionsExportAccounts) - .presentation(subscriptionsExportDocumentPicker) - } - .foregroundColor(.primary) +// Section(header: Text("DATABASE")) { +// Picker(selection: $viewModel.refreshInterval, label: Text("Refresh Interval")) { +// ForEach(RefreshInterval.allCases.identified(by: \.self)) { interval in +// Text(interval.description()).tag(interval) +// } +// } +// Button(action: { +// self.subscriptionsImportAccounts = self.createSubscriptionsImportAccounts +// }) { +// Text("Import Subscriptions...") +// } +// .presentation(subscriptionsImportAccounts) +// .presentation(subscriptionsImportDocumentPicker) +// Button(action: { +// self.subscriptionsExportAccounts = self.createSubscriptionsExportAccounts +// }) { +// Text("Export Subscriptions...") +// } +// .presentation(subscriptionsExportAccounts) +// .presentation(subscriptionsExportDocumentPicker) +// } +// .foregroundColor(.primary) Section(header: Text("ABOUT"), footer: buildFooter) { Text("About NetNewsWire") - PresentationButton(destination: SafariView(url: URL(string: "https://ranchero.com/netnewswire/")!)) { + + Button(action: { self.isWebsitePresented.toggle() }) { Text("Website") + } + }.sheet(isPresented: $isWebsitePresented) { + SafariView(url: URL(string: "https://ranchero.com/netnewswire/")!) } - PresentationButton(destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire")!)) { - Text("Github Repository") + + VStack { + Button(action: { self.isGithubRepoPresented.toggle() }) { + Text("Github Repository") + } + }.sheet(isPresented: $isGithubRepoPresented) { + SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire")!) } - PresentationButton(destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!)) { - Text("Bug Tracker") + + VStack { + Button(action: { self.isBugTrackerPresented.toggle() }) { + Text("Bug Tracker") + } + }.sheet(isPresented: $isBugTrackerPresented) { + SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!) } - PresentationButton(destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")!)) { - Text("Technotes") + + VStack { + Button(action: { self.isTechnotesPresented.toggle() }) { + Text("Technotes") + } + }.sheet(isPresented: $isTechnotesPresented) { + SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")!) } - PresentationButton(destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown")!)) { - Text("How to Support NetNewsWire") + + VStack { + Button(action: { self.isHowToSupportPresented.toggle() }) { + Text("Technotes") + } + }.sheet(isPresented: $isHowToSupportPresented) { + SafariView(url: URL(string: "hhttps://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown")!) } + Text("Add NetNewsWire News Feed") - } - .foregroundColor(.primary) + + } + .foregroundColor(.primary) } .navigationBarTitle(Text("Settings"), displayMode: .inline) - } } - var createSubscriptionsImportAccounts: ActionSheet { - var buttons = [ActionSheet.Button]() - - for account in viewModel.activeAccounts { - if !account.isOPMLImportSupported { - continue - } - - let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) { - self.subscriptionsImportAccounts = nil - self.subscriptionsImportDocumentPicker = Modal(SettingsSubscriptionsImportDocumentPickerView(account: account)) - } - - buttons.append(button) - } - - buttons.append(.cancel { self.subscriptionsImportAccounts = nil }) - return ActionSheet(title: Text("Import Subscriptions..."), message: Text("Select the account to import your OPML file into."), buttons: buttons) - } - - var createSubscriptionsExportAccounts: ActionSheet { - var buttons = [ActionSheet.Button]() - - for account in viewModel.accounts { - let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) { - self.subscriptionsExportAccounts = nil - self.subscriptionsExportDocumentPicker = Modal(SettingsSubscriptionsExportDocumentPickerView(account: account)) - } - buttons.append(button) - } - - buttons.append(.cancel { self.subscriptionsExportAccounts = nil }) - return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons) - } +// var createSubscriptionsImportAccounts: ActionSheet { +// var buttons = [ActionSheet.Button]() +// +// for account in viewModel.activeAccounts { +// if !account.isOPMLImportSupported { +// continue +// } +// +// let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) { +// self.subscriptionsImportAccounts = nil +// self.subscriptionsImportDocumentPicker = Modal(SettingsSubscriptionsImportDocumentPickerView(account: account)) +// } +// +// buttons.append(button) +// } +// +// buttons.append(.cancel { self.subscriptionsImportAccounts = nil }) +// return ActionSheet(title: Text("Import Subscriptions..."), message: Text("Select the account to import your OPML file into."), buttons: buttons) +// } +// +// var createSubscriptionsExportAccounts: ActionSheet { +// var buttons = [ActionSheet.Button]() +// +// for account in viewModel.accounts { +// let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) { +// self.subscriptionsExportAccounts = nil +// self.subscriptionsExportDocumentPicker = Modal(SettingsSubscriptionsExportDocumentPickerView(account: account)) +// } +// buttons.append(button) +// } +// +// buttons.append(.cancel { self.subscriptionsExportAccounts = nil }) +// return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons) +// } var buildFooter: some View { return Text(verbatim: "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))") @@ -134,9 +162,9 @@ struct SettingsView : View { // MARK: ViewModel - class ViewModel: BindableObject { + class ViewModel: ObservableObject { - let didChange = PassthroughSubject() + let objectWillChange = ObservableObjectPublisher() init() { NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .AccountsDidChange, object: nil) @@ -164,12 +192,12 @@ struct SettingsView : View { return AppDefaults.timelineSortDirection == .orderedDescending } set { + objectWillChange.send() if newValue == true { AppDefaults.timelineSortDirection = .orderedDescending } else { AppDefaults.timelineSortDirection = .orderedAscending } - didChange.send(self) } } @@ -178,8 +206,8 @@ struct SettingsView : View { return AppDefaults.timelineNumberOfLines } set { + objectWillChange.send() AppDefaults.timelineNumberOfLines = newValue - didChange.send(self) } } @@ -188,17 +216,17 @@ struct SettingsView : View { return AppDefaults.refreshInterval } set { + objectWillChange.send() AppDefaults.refreshInterval = newValue - didChange.send(self) } } @objc func accountsDidChange(_ notification: Notification) { - didChange.send(self) + objectWillChange.send() } @objc func displayNameDidChange(_ notification: Notification) { - didChange.send(self) + objectWillChange.send() } }