mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2024-12-22 23:58:36 +01:00
Add basic multi window support to iOS
This commit is contained in:
parent
55ab50289c
commit
178e89b1fb
@ -47,6 +47,7 @@
|
||||
5183CCED22711DCE0010922C /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5183CCEC22711DCE0010922C /* Settings.storyboard */; };
|
||||
5183CCEF227125970010922C /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCEE227125970010922C /* SettingsViewController.swift */; };
|
||||
519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519B8D322143397200FA689C /* SharingServiceDelegate.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, ); }; };
|
||||
51C451B9226377C900C03939 /* Articles.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 840716732262A60F00344432 /* Articles.framework */; };
|
||||
@ -695,6 +696,7 @@
|
||||
5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsImportDocumentPickerView.swift; sourceTree = "<group>"; };
|
||||
5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsExportDocumentPickerView.swift; sourceTree = "<group>"; };
|
||||
519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = "<group>"; };
|
||||
519E743422C663F900A78E47 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard-Extensions.swift"; sourceTree = "<group>"; };
|
||||
51C45250226506F400C03939 /* String-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String-Extensions.swift"; sourceTree = "<group>"; };
|
||||
51C45254226507D200C03939 /* AppAssets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppAssets.swift; sourceTree = "<group>"; };
|
||||
@ -1706,6 +1708,7 @@
|
||||
84C9FCA22262A1B800D921D6 /* LaunchScreen.storyboard */,
|
||||
84C9FC9F2262A1B300D921D6 /* Main.storyboard */,
|
||||
840D617E2029031C009BC708 /* AppDelegate.swift */,
|
||||
519E743422C663F900A78E47 /* SceneDelegate.swift */,
|
||||
51C45254226507D200C03939 /* AppAssets.swift */,
|
||||
51C45255226507D200C03939 /* AppDefaults.swift */,
|
||||
51E3EB3C229AB08300645299 /* ErrorHandler.swift */,
|
||||
@ -1963,12 +1966,12 @@
|
||||
ORGANIZATIONNAME = "Ranchero Software";
|
||||
TargetAttributes = {
|
||||
6581C73220CED60000F4AD34 = {
|
||||
DevelopmentTeam = 96VR936H35;
|
||||
ProvisioningStyle = Automatic;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
840D617B2029031C009BC708 = {
|
||||
CreatedOnToolsVersion = 9.3;
|
||||
DevelopmentTeam = 96VR936H35;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
SystemCapabilities = {
|
||||
com.apple.BackgroundModes = {
|
||||
@ -1978,8 +1981,8 @@
|
||||
};
|
||||
849C645F1ED37A5D003D8FC0 = {
|
||||
CreatedOnToolsVersion = 8.2.1;
|
||||
DevelopmentTeam = 96VR936H35;
|
||||
ProvisioningStyle = Automatic;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Manual;
|
||||
SystemCapabilities = {
|
||||
com.apple.HardenedRuntime = {
|
||||
enabled = 1;
|
||||
@ -1988,7 +1991,7 @@
|
||||
};
|
||||
849C64701ED37A5D003D8FC0 = {
|
||||
CreatedOnToolsVersion = 8.2.1;
|
||||
DevelopmentTeam = 96VR936H35;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
TestTargetID = 849C645F1ED37A5D003D8FC0;
|
||||
};
|
||||
@ -2397,6 +2400,7 @@
|
||||
51C45268226508F600C03939 /* MasterFeedUnreadCountView.swift in Sources */,
|
||||
5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */,
|
||||
51C4529F22650A1900C03939 /* AuthorAvatarDownloader.swift in Sources */,
|
||||
519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */,
|
||||
51E595AD228E1C2100FCC42B /* AddAccountViewController.swift in Sources */,
|
||||
51C452A322650A1E00C03939 /* HTMLMetadataDownloader.swift in Sources */,
|
||||
51C4528D2265095F00C03939 /* AddFolderViewController.swift in Sources */,
|
||||
|
@ -33,7 +33,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
||||
}
|
||||
|
||||
var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "application")
|
||||
var window: UIWindow?
|
||||
|
||||
var faviconDownloader: FaviconDownloader!
|
||||
var imageDownloader: ImageDownloader!
|
||||
@ -67,15 +66,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
||||
|
||||
registerBackgroundTasks()
|
||||
|
||||
// Set up the split view
|
||||
let splitViewController = window!.rootViewController as! UISplitViewController
|
||||
|
||||
let detailNavController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
|
||||
detailNavController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
|
||||
splitViewController.delegate = self
|
||||
|
||||
window!.tintColor = AppAssets.netNewsWireBlueColor
|
||||
|
||||
AppDefaults.registerDefaults()
|
||||
let isFirstRun = AppDefaults.isFirstRun
|
||||
if isFirstRun {
|
||||
@ -119,31 +109,29 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
||||
|
||||
}
|
||||
|
||||
// func application(_ application: UIApplication, shouldSaveApplicationState coder: NSCoder) -> Bool {
|
||||
//
|
||||
// let versionNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
|
||||
// coder.encode(versionNumber, forKey: "VersionNumber")
|
||||
//
|
||||
// return true
|
||||
//
|
||||
// }
|
||||
//
|
||||
// func application(_ application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool {
|
||||
// if let storedVersionNumber = coder.decodeObject(forKey: "VersionNumber") as? String {
|
||||
// let versionNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
|
||||
// if versionNumber == storedVersionNumber {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
|
||||
func applicationWillResignActive(_ application: UIApplication) {
|
||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
shuttingDown = true
|
||||
}
|
||||
|
||||
func applicationDidEnterBackground(_ application: UIApplication) {
|
||||
// MARK: Notifications
|
||||
|
||||
@objc func unreadCountDidChange(_ note: Notification) {
|
||||
if note.object is AccountManager {
|
||||
unreadCount = AccountManager.shared.unreadCount
|
||||
}
|
||||
}
|
||||
|
||||
@objc func userDefaultsDidChange(_ note: Notification) {
|
||||
scheduleBackgroundFeedRefresh()
|
||||
}
|
||||
|
||||
@objc func accountRefreshDidFinish(_ note: Notification) {
|
||||
AppDefaults.lastRefresh = Date()
|
||||
}
|
||||
|
||||
// MARK: - API
|
||||
|
||||
func prepareAccountsForBackground() {
|
||||
syncTimer?.invalidate()
|
||||
|
||||
// Schedule background app refresh
|
||||
@ -169,57 +157,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
||||
}
|
||||
}
|
||||
|
||||
func applicationWillEnterForeground(_ application: UIApplication) {
|
||||
AccountManager.shared.syncArticleStatusAll()
|
||||
syncTimer?.update()
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(_ application: UIApplication) {
|
||||
// If we haven't refreshed the database for 15 minutes, run a refresh automatically
|
||||
func prepareAccountsForForeground() {
|
||||
if let lastRefresh = AppDefaults.lastRefresh {
|
||||
if Date() > lastRefresh.addingTimeInterval(15 * 60) {
|
||||
AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log)
|
||||
} else {
|
||||
AccountManager.shared.syncArticleStatusAll()
|
||||
syncTimer?.update()
|
||||
}
|
||||
} else {
|
||||
AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
shuttingDown = true
|
||||
}
|
||||
|
||||
// MARK: - Split view
|
||||
|
||||
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
|
||||
guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }
|
||||
guard let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController else { return false }
|
||||
if topAsDetailController.navState?.currentArticle == nil {
|
||||
// Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
@objc func unreadCountDidChange(_ note: Notification) {
|
||||
if note.object is AccountManager {
|
||||
unreadCount = AccountManager.shared.unreadCount
|
||||
}
|
||||
}
|
||||
|
||||
@objc func userDefaultsDidChange(_ note: Notification) {
|
||||
scheduleBackgroundFeedRefresh()
|
||||
}
|
||||
|
||||
@objc func accountRefreshDidFinish(_ note: Notification) {
|
||||
AppDefaults.lastRefresh = Date()
|
||||
}
|
||||
|
||||
// MARK: - API
|
||||
|
||||
func logMessage(_ message: String, type: LogItem.ItemType) {
|
||||
print("logMessage: \(message) - \(type)")
|
||||
|
||||
|
@ -52,14 +52,31 @@
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<true/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UISceneConfigurationName</key>
|
||||
<string>Default Configuration</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>NetNewsWire.SceneDelegate</string>
|
||||
<key>UISceneStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
|
83
iOS/SceneDelegate.swift
Normal file
83
iOS/SceneDelegate.swift
Normal file
@ -0,0 +1,83 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 6/28/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate, UISplitViewControllerDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
// UIWindowScene delegate
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||
|
||||
window!.tintColor = AppAssets.netNewsWireBlueColor
|
||||
|
||||
let storyboard = UIStoryboard(name: "Main", bundle: .main)
|
||||
let splitViewController = storyboard.instantiateInitialViewController() as! UISplitViewController
|
||||
splitViewController.delegate = self
|
||||
window!.rootViewController = splitViewController
|
||||
|
||||
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
|
||||
navigationController.topViewController!.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem
|
||||
|
||||
|
||||
// if let userActivity = connectionOptions.userActivities.first ?? session.stateRestorationActivity {
|
||||
// if !configure(window: window, with: userActivity) {
|
||||
// print("Failed to restore from \(userActivity)")
|
||||
// }
|
||||
// }
|
||||
|
||||
// If there were no user activities, we don't have to do anything.
|
||||
// The `window` property will automatically be loaded with the storyboard's initial view controller.
|
||||
}
|
||||
|
||||
func sceneDidEnterBackground(_ scene: UIScene) {
|
||||
appDelegate.prepareAccountsForBackground()
|
||||
}
|
||||
|
||||
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||
appDelegate.prepareAccountsForForeground()
|
||||
}
|
||||
|
||||
// func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? {
|
||||
// return scene.userActivity
|
||||
// }
|
||||
//
|
||||
// Utilities
|
||||
|
||||
// func configure(window: UIWindow?, with activity: NSUserActivity) -> Bool {
|
||||
// if activity.title == GalleryOpenDetailPath {
|
||||
// if let photoID = activity.userInfo?[GalleryOpenDetailPhotoIdKey] as? String {
|
||||
//
|
||||
// if let photoDetailViewController = PhotoDetailViewController.loadFromStoryboard() {
|
||||
// photoDetailViewController.photo = Photo(name: photoID)
|
||||
//
|
||||
// if let navigationController = window?.rootViewController as? UINavigationController {
|
||||
// navigationController.pushViewController(photoDetailViewController, animated: false)
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
|
||||
// MARK: UISplitViewControllerDelegate
|
||||
|
||||
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
|
||||
guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }
|
||||
guard let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController else { return false }
|
||||
if topAsDetailController.navState?.currentArticle == nil {
|
||||
// Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user