Restore several of Maurice’s changes, including the new three-pane split view.
This commit is contained in:
parent
f54fe338cd
commit
61496e138f
@ -299,7 +299,7 @@
|
|||||||
51C45268226508F600C03939 /* FeedUnreadCountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C45261226508F600C03939 /* FeedUnreadCountView.swift */; };
|
51C45268226508F600C03939 /* FeedUnreadCountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C45261226508F600C03939 /* FeedUnreadCountView.swift */; };
|
||||||
51C45269226508F600C03939 /* FeedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C45262226508F600C03939 /* FeedTableViewCell.swift */; };
|
51C45269226508F600C03939 /* FeedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C45262226508F600C03939 /* FeedTableViewCell.swift */; };
|
||||||
51C4526A226508F600C03939 /* FeedTableViewCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C45263226508F600C03939 /* FeedTableViewCellLayout.swift */; };
|
51C4526A226508F600C03939 /* FeedTableViewCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C45263226508F600C03939 /* FeedTableViewCellLayout.swift */; };
|
||||||
51C4526B226508F600C03939 /* FeedsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C45264226508F600C03939 /* FeedsViewController.swift */; };
|
51C4526B226508F600C03939 /* SidebarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C45264226508F600C03939 /* SidebarViewController.swift */; };
|
||||||
51C452762265091600C03939 /* TimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C4526E2265091600C03939 /* TimelineViewController.swift */; };
|
51C452762265091600C03939 /* TimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C4526E2265091600C03939 /* TimelineViewController.swift */; };
|
||||||
51C452772265091600C03939 /* MultilineUILabelSizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C452702265091600C03939 /* MultilineUILabelSizer.swift */; };
|
51C452772265091600C03939 /* MultilineUILabelSizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C452702265091600C03939 /* MultilineUILabelSizer.swift */; };
|
||||||
51C452782265091600C03939 /* TimelineCellData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C452712265091600C03939 /* TimelineCellData.swift */; };
|
51C452782265091600C03939 /* TimelineCellData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C452712265091600C03939 /* TimelineCellData.swift */; };
|
||||||
@ -1241,7 +1241,7 @@
|
|||||||
51C45261226508F600C03939 /* FeedUnreadCountView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedUnreadCountView.swift; sourceTree = "<group>"; };
|
51C45261226508F600C03939 /* FeedUnreadCountView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedUnreadCountView.swift; sourceTree = "<group>"; };
|
||||||
51C45262226508F600C03939 /* FeedTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedTableViewCell.swift; sourceTree = "<group>"; };
|
51C45262226508F600C03939 /* FeedTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
51C45263226508F600C03939 /* FeedTableViewCellLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedTableViewCellLayout.swift; sourceTree = "<group>"; };
|
51C45263226508F600C03939 /* FeedTableViewCellLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedTableViewCellLayout.swift; sourceTree = "<group>"; };
|
||||||
51C45264226508F600C03939 /* FeedsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedsViewController.swift; sourceTree = "<group>"; };
|
51C45264226508F600C03939 /* SidebarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SidebarViewController.swift; sourceTree = "<group>"; };
|
||||||
51C4526E2265091600C03939 /* TimelineViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineViewController.swift; sourceTree = "<group>"; };
|
51C4526E2265091600C03939 /* TimelineViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineViewController.swift; sourceTree = "<group>"; };
|
||||||
51C452702265091600C03939 /* MultilineUILabelSizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultilineUILabelSizer.swift; sourceTree = "<group>"; };
|
51C452702265091600C03939 /* MultilineUILabelSizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultilineUILabelSizer.swift; sourceTree = "<group>"; };
|
||||||
51C452712265091600C03939 /* TimelineCellData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineCellData.swift; sourceTree = "<group>"; };
|
51C452712265091600C03939 /* TimelineCellData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineCellData.swift; sourceTree = "<group>"; };
|
||||||
@ -1950,7 +1950,7 @@
|
|||||||
51C4525D226508F600C03939 /* Feeds */ = {
|
51C4525D226508F600C03939 /* Feeds */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
51C45264226508F600C03939 /* FeedsViewController.swift */,
|
51C45264226508F600C03939 /* SidebarViewController.swift */,
|
||||||
51627A6623861DA3007B3B4B /* FeedsViewController+Drag.swift */,
|
51627A6623861DA3007B3B4B /* FeedsViewController+Drag.swift */,
|
||||||
51627A6823861DED007B3B4B /* FeedsViewController+Drop.swift */,
|
51627A6823861DED007B3B4B /* FeedsViewController+Drop.swift */,
|
||||||
51CE1C0A23622006005548FC /* RefreshProgressView.swift */,
|
51CE1C0A23622006005548FC /* RefreshProgressView.swift */,
|
||||||
@ -3961,7 +3961,7 @@
|
|||||||
51C4525C226508DF00C03939 /* String-Extensions.swift in Sources */,
|
51C4525C226508DF00C03939 /* String-Extensions.swift in Sources */,
|
||||||
51F9F3F923DFB16300A314FD /* UITableView-Extensions.swift in Sources */,
|
51F9F3F923DFB16300A314FD /* UITableView-Extensions.swift in Sources */,
|
||||||
51C452792265091600C03939 /* TimelineTableViewCell.swift in Sources */,
|
51C452792265091600C03939 /* TimelineTableViewCell.swift in Sources */,
|
||||||
51C4526B226508F600C03939 /* FeedsViewController.swift in Sources */,
|
51C4526B226508F600C03939 /* SidebarViewController.swift in Sources */,
|
||||||
5126EE97226CB48A00C22AFC /* SceneCoordinator.swift in Sources */,
|
5126EE97226CB48A00C22AFC /* SceneCoordinator.swift in Sources */,
|
||||||
84CAFCB022BC8C35007694F0 /* FetchRequestOperation.swift in Sources */,
|
84CAFCB022BC8C35007694F0 /* FetchRequestOperation.swift in Sources */,
|
||||||
51EF0F77227716200050506E /* FaviconGenerator.swift in Sources */,
|
51EF0F77227716200050506E /* FaviconGenerator.swift in Sources */,
|
||||||
|
@ -10,8 +10,10 @@ import Foundation
|
|||||||
import Articles
|
import Articles
|
||||||
|
|
||||||
extension Notification.Name {
|
extension Notification.Name {
|
||||||
|
|
||||||
static let InspectableObjectsDidChange = Notification.Name("TimelineSelectionDidChangeNotification")
|
static let InspectableObjectsDidChange = Notification.Name("TimelineSelectionDidChangeNotification")
|
||||||
static let UserDidAddFeed = Notification.Name("UserDidAddFeedNotification")
|
static let UserDidAddFeed = Notification.Name("UserDidAddFeedNotification")
|
||||||
|
static let LaunchedFromExternalAction = Notification.Name("LaunchedFromExternalAction")
|
||||||
|
|
||||||
#if !MAC_APP_STORE
|
#if !MAC_APP_STORE
|
||||||
static let WebInspectorEnabledDidChange = Notification.Name("WebInspectorEnabledDidChange")
|
static let WebInspectorEnabledDidChange = Notification.Name("WebInspectorEnabledDidChange")
|
||||||
|
@ -25,5 +25,6 @@ struct UserInfoKey {
|
|||||||
static let selectedFeedsState = "selectedFeedsState"
|
static let selectedFeedsState = "selectedFeedsState"
|
||||||
static let isShowingExtractedArticle = "isShowingExtractedArticle"
|
static let isShowingExtractedArticle = "isShowingExtractedArticle"
|
||||||
static let articleWindowScrollY = "articleWindowScrollY"
|
static let articleWindowScrollY = "articleWindowScrollY"
|
||||||
|
static let isSidebarHidden = "isSidebarHidden"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,8 @@ class ArticleViewController: UIViewController {
|
|||||||
|
|
||||||
weak var coordinator: SceneCoordinator!
|
weak var coordinator: SceneCoordinator!
|
||||||
|
|
||||||
|
private let poppableDelegate = PoppableGestureRecognizerDelegate()
|
||||||
|
|
||||||
var article: Article? {
|
var article: Article? {
|
||||||
didSet {
|
didSet {
|
||||||
if let controller = currentWebViewController, controller.article != article {
|
if let controller = currentWebViewController, controller.article != article {
|
||||||
@ -82,6 +84,8 @@ class ArticleViewController: UIViewController {
|
|||||||
return keyboardManager.keyCommands
|
return keyboardManager.keyCommands
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var lastKnownDisplayMode : UISplitViewController.DisplayMode?
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
@ -100,7 +104,14 @@ class ArticleViewController: UIViewController {
|
|||||||
|
|
||||||
articleExtractorButton.addTarget(self, action: #selector(toggleArticleExtractor(_:)), for: .touchUpInside)
|
articleExtractorButton.addTarget(self, action: #selector(toggleArticleExtractor(_:)), for: .touchUpInside)
|
||||||
toolbarItems?.insert(UIBarButtonItem(customView: articleExtractorButton), at: 6)
|
toolbarItems?.insert(UIBarButtonItem(customView: articleExtractorButton), at: 6)
|
||||||
|
|
||||||
|
if let parentNavController = navigationController?.parent as? UINavigationController {
|
||||||
|
poppableDelegate.navigationController = parentNavController
|
||||||
|
parentNavController.interactivePopGestureRecognizer?.delegate = poppableDelegate
|
||||||
|
}
|
||||||
|
|
||||||
|
navigationItem.leftItemsSupplementBackButton = true
|
||||||
|
|
||||||
pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:])
|
pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:])
|
||||||
pageViewController.delegate = self
|
pageViewController.delegate = self
|
||||||
pageViewController.dataSource = self
|
pageViewController.dataSource = self
|
||||||
@ -155,12 +166,17 @@ class ArticleViewController: UIViewController {
|
|||||||
updateUI()
|
updateUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewDidAppear(true)
|
navigationController?.isToolbarHidden = false
|
||||||
coordinator.isArticleViewControllerPending = false
|
if AppDefaults.shared.articleFullscreenEnabled {
|
||||||
|
currentWebViewController?.hideBars()
|
||||||
|
}
|
||||||
|
|
||||||
|
super.viewWillAppear(animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillDisappear(_ animated: Bool) {
|
override func viewWillDisappear(_ animated: Bool) {
|
||||||
|
super.viewWillDisappear(animated)
|
||||||
if searchBar != nil && !searchBar.isHidden {
|
if searchBar != nil && !searchBar.isHidden {
|
||||||
endFind()
|
endFind()
|
||||||
}
|
}
|
||||||
@ -211,9 +227,12 @@ class ArticleViewController: UIViewController {
|
|||||||
starBarButtonItem.image = AppAssets.starOpenImage
|
starBarButtonItem.image = AppAssets.starOpenImage
|
||||||
starBarButtonItem.accLabelText = NSLocalizedString("Star Article", comment: "Star Article")
|
starBarButtonItem.accLabelText = NSLocalizedString("Star Article", comment: "Star Article")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func contentScrollView(for edge: NSDirectionalRectEdge) -> UIScrollView? {
|
||||||
|
return currentWebViewController?.webView?.scrollView
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Notifications
|
// MARK: Notifications
|
||||||
|
|
||||||
@objc dynamic func unreadCountDidChange(_ notification: Notification) {
|
@objc dynamic func unreadCountDidChange(_ notification: Notification) {
|
||||||
@ -324,6 +343,10 @@ class ArticleViewController: UIViewController {
|
|||||||
func setScrollPosition(isShowingExtractedArticle: Bool, articleWindowScrollY: Int) {
|
func setScrollPosition(isShowingExtractedArticle: Bool, articleWindowScrollY: Int) {
|
||||||
currentWebViewController?.setScrollPosition(isShowingExtractedArticle: isShowingExtractedArticle, articleWindowScrollY: articleWindowScrollY)
|
currentWebViewController?.setScrollPosition(isShowingExtractedArticle: isShowingExtractedArticle, articleWindowScrollY: articleWindowScrollY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func splitViewControllerWillChangeTo(displayMode: UISplitViewController.DisplayMode) {
|
||||||
|
lastKnownDisplayMode = displayMode
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Find in Article
|
// MARK: Find in Article
|
||||||
@ -447,9 +470,11 @@ extension ArticleViewController: UIPageViewControllerDelegate {
|
|||||||
coordinator.selectArticle(article, animations: [.select, .scroll, .navigation])
|
coordinator.selectArticle(article, animations: [.select, .scroll, .navigation])
|
||||||
articleExtractorButton.buttonState = currentWebViewController?.articleExtractorButtonState ?? .off
|
articleExtractorButton.buttonState = currentWebViewController?.articleExtractorButtonState ?? .off
|
||||||
|
|
||||||
previousViewControllers.compactMap({ $0 as? WebViewController }).forEach({ $0.stopWebViewActivity() })
|
let webViewControllers = previousViewControllers.compactMap{ $0 as? WebViewController }
|
||||||
|
for webViewController in webViewControllers {
|
||||||
|
webViewController.stopWebViewActivity()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: UIGestureRecognizerDelegate
|
// MARK: UIGestureRecognizerDelegate
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Cqo-6I-B1A">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Cqo-6I-B1A">
|
||||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/>
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
@ -14,8 +16,8 @@
|
|||||||
<view key="view" contentMode="scaleToFill" id="wsU-Ys-fCl">
|
<view key="view" contentMode="scaleToFill" id="wsU-Ys-fCl">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
|
||||||
<viewLayoutGuide key="safeArea" id="DpR-ol-ipa"/>
|
<viewLayoutGuide key="safeArea" id="DpR-ol-ipa"/>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
</view>
|
</view>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="KWL-WN-CvS" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="KWL-WN-CvS" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
@ -33,7 +35,7 @@
|
|||||||
<tableViewSection id="iGa-Kt-kXs">
|
<tableViewSection id="iGa-Kt-kXs">
|
||||||
<cells>
|
<cells>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="kLN-6k-tr4">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="kLN-6k-tr4">
|
||||||
<rect key="frame" x="0.0" y="28" width="414" height="43.5"/>
|
<rect key="frame" x="0.0" y="50" width="414" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="kLN-6k-tr4" id="f8N-5M-qt0">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="kLN-6k-tr4" id="f8N-5M-qt0">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||||
@ -41,7 +43,7 @@
|
|||||||
</tableViewCellContentView>
|
</tableViewCellContentView>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="ToK-3P-DtT">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="ToK-3P-DtT">
|
||||||
<rect key="frame" x="0.0" y="71.5" width="414" height="43.5"/>
|
<rect key="frame" x="0.0" y="93.5" width="414" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ToK-3P-DtT" id="HWf-pi-1Bf">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ToK-3P-DtT" id="HWf-pi-1Bf">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||||
@ -49,7 +51,7 @@
|
|||||||
</tableViewCellContentView>
|
</tableViewCellContentView>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="3uf-lI-yxD">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="3uf-lI-yxD">
|
||||||
<rect key="frame" x="0.0" y="115" width="414" height="43.5"/>
|
<rect key="frame" x="0.0" y="137" width="414" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3uf-lI-yxD" id="MuP-Hv-Zfw">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3uf-lI-yxD" id="MuP-Hv-Zfw">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||||
@ -65,12 +67,19 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</tableView>
|
</tableView>
|
||||||
<toolbarItems>
|
<toolbarItems>
|
||||||
<barButtonItem title="Item" image="gear" catalog="system" id="AK3-N5-4ke"/>
|
<barButtonItem title="Item" image="gear" catalog="system" id="AK3-N5-4ke">
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Settings bar button item"/>
|
||||||
|
</attributedString>
|
||||||
|
</barButtonItem>
|
||||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="Kjl-Sb-QP1"/>
|
<barButtonItem style="plain" systemItem="flexibleSpace" id="Kjl-Sb-QP1"/>
|
||||||
<barButtonItem systemItem="add" id="PVr-3K-nPg"/>
|
<barButtonItem systemItem="add" id="PVr-3K-nPg"/>
|
||||||
</toolbarItems>
|
</toolbarItems>
|
||||||
<navigationItem key="navigationItem" title="Feeds" largeTitleDisplayMode="never" id="lE1-xw-gjH">
|
<navigationItem key="navigationItem" title="Feeds" largeTitleDisplayMode="never" id="lE1-xw-gjH">
|
||||||
<barButtonItem key="rightBarButtonItem" image="line.3.horizontal.decrease.circle" catalog="system" id="Khk-Hd-iNS"/>
|
<barButtonItem key="rightBarButtonItem" image="line.3.horizontal.decrease.circle" catalog="system" id="Khk-Hd-iNS"/>
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Navigation bar title: Feeds"/>
|
||||||
|
</attributedString>
|
||||||
</navigationItem>
|
</navigationItem>
|
||||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||||
</tableViewController>
|
</tableViewController>
|
||||||
@ -83,7 +92,7 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<navigationController toolbarHidden="NO" id="oiG-nz-IQ0" sceneMemberID="viewController">
|
<navigationController toolbarHidden="NO" id="oiG-nz-IQ0" sceneMemberID="viewController">
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="KVh-c4-2Fm">
|
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="KVh-c4-2Fm">
|
||||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</navigationBar>
|
</navigationBar>
|
||||||
<toolbar key="toolbar" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="WeP-vu-EcM">
|
<toolbar key="toolbar" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="WeP-vu-EcM">
|
||||||
@ -113,7 +122,10 @@
|
|||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="gear" catalog="system" width="64" height="58"/>
|
<image name="gear" catalog="system" width="128" height="122"/>
|
||||||
<image name="line.3.horizontal.decrease.circle" catalog="system" width="64" height="60"/>
|
<image name="line.3.horizontal.decrease.circle" catalog="system" width="128" height="123"/>
|
||||||
|
<systemColor name="systemBackgroundColor">
|
||||||
|
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</systemColor>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Cqo-6I-B1A">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Cqo-6I-B1A">
|
||||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/>
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
@ -14,8 +16,8 @@
|
|||||||
<view key="view" contentMode="scaleToFill" id="wsU-Ys-fCl">
|
<view key="view" contentMode="scaleToFill" id="wsU-Ys-fCl">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
|
||||||
<viewLayoutGuide key="safeArea" id="DpR-ol-ipa"/>
|
<viewLayoutGuide key="safeArea" id="DpR-ol-ipa"/>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
</view>
|
</view>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="KWL-WN-CvS" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="KWL-WN-CvS" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
@ -33,7 +35,7 @@
|
|||||||
<tableViewSection id="iGa-Kt-kXs">
|
<tableViewSection id="iGa-Kt-kXs">
|
||||||
<cells>
|
<cells>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="kLN-6k-tr4">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="kLN-6k-tr4">
|
||||||
<rect key="frame" x="0.0" y="28" width="414" height="43.5"/>
|
<rect key="frame" x="0.0" y="50" width="414" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="kLN-6k-tr4" id="f8N-5M-qt0">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="kLN-6k-tr4" id="f8N-5M-qt0">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||||
@ -41,7 +43,7 @@
|
|||||||
</tableViewCellContentView>
|
</tableViewCellContentView>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="ToK-3P-DtT">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="ToK-3P-DtT">
|
||||||
<rect key="frame" x="0.0" y="71.5" width="414" height="43.5"/>
|
<rect key="frame" x="0.0" y="93.5" width="414" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ToK-3P-DtT" id="HWf-pi-1Bf">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ToK-3P-DtT" id="HWf-pi-1Bf">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||||
@ -49,7 +51,7 @@
|
|||||||
</tableViewCellContentView>
|
</tableViewCellContentView>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="3uf-lI-yxD">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="3uf-lI-yxD">
|
||||||
<rect key="frame" x="0.0" y="115" width="414" height="43.5"/>
|
<rect key="frame" x="0.0" y="137" width="414" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3uf-lI-yxD" id="MuP-Hv-Zfw">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3uf-lI-yxD" id="MuP-Hv-Zfw">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||||
@ -65,12 +67,19 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</tableView>
|
</tableView>
|
||||||
<toolbarItems>
|
<toolbarItems>
|
||||||
<barButtonItem title="Item" image="gear" catalog="system" id="AK3-N5-4ke"/>
|
<barButtonItem title="Item" image="gear" catalog="system" id="AK3-N5-4ke">
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Bar button item: Settings"/>
|
||||||
|
</attributedString>
|
||||||
|
</barButtonItem>
|
||||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="Kjl-Sb-QP1"/>
|
<barButtonItem style="plain" systemItem="flexibleSpace" id="Kjl-Sb-QP1"/>
|
||||||
<barButtonItem systemItem="add" id="PVr-3K-nPg"/>
|
<barButtonItem systemItem="add" id="PVr-3K-nPg"/>
|
||||||
</toolbarItems>
|
</toolbarItems>
|
||||||
<navigationItem key="navigationItem" title="Feeds" largeTitleDisplayMode="always" id="lE1-xw-gjH">
|
<navigationItem key="navigationItem" title="Feeds" largeTitleDisplayMode="always" id="lE1-xw-gjH">
|
||||||
<barButtonItem key="rightBarButtonItem" image="line.3.horizontal.decrease.circle" catalog="system" id="Khk-Hd-iNS"/>
|
<barButtonItem key="rightBarButtonItem" image="line.3.horizontal.decrease.circle" catalog="system" id="Khk-Hd-iNS"/>
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Navigation bar title: Feeds"/>
|
||||||
|
</attributedString>
|
||||||
</navigationItem>
|
</navigationItem>
|
||||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||||
</tableViewController>
|
</tableViewController>
|
||||||
@ -83,7 +92,7 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<navigationController toolbarHidden="NO" id="oiG-nz-IQ0" sceneMemberID="viewController">
|
<navigationController toolbarHidden="NO" id="oiG-nz-IQ0" sceneMemberID="viewController">
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="KVh-c4-2Fm">
|
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="KVh-c4-2Fm">
|
||||||
<rect key="frame" x="0.0" y="44" width="414" height="96"/>
|
<rect key="frame" x="0.0" y="48" width="414" height="96"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</navigationBar>
|
</navigationBar>
|
||||||
<toolbar key="toolbar" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="WeP-vu-EcM">
|
<toolbar key="toolbar" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="WeP-vu-EcM">
|
||||||
@ -113,7 +122,10 @@
|
|||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="gear" catalog="system" width="64" height="58"/>
|
<image name="gear" catalog="system" width="128" height="122"/>
|
||||||
<image name="line.3.horizontal.decrease.circle" catalog="system" width="64" height="60"/>
|
<image name="line.3.horizontal.decrease.circle" catalog="system" width="128" height="123"/>
|
||||||
|
<systemColor name="systemBackgroundColor">
|
||||||
|
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</systemColor>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="7bK-jq-Zjz">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="AJQ-jq-uMa">
|
||||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/>
|
||||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||||
@ -12,12 +13,12 @@
|
|||||||
<!--Article-->
|
<!--Article-->
|
||||||
<scene sceneID="yUG-lL-AsK">
|
<scene sceneID="yUG-lL-AsK">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController storyboardIdentifier="ArticleViewController" title="Detail" useStoryboardIdentifierAsRestorationIdentifier="YES" id="JEX-9P-axG" userLabel="Article" customClass="ArticleViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController storyboardIdentifier="ArticleViewController" title="Detail" extendedLayoutIncludesOpaqueBars="YES" useStoryboardIdentifierAsRestorationIdentifier="YES" id="JEX-9P-axG" userLabel="Article" customClass="ArticleViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="svH-Pt-448">
|
<view key="view" contentMode="scaleToFill" id="svH-Pt-448">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view hidden="YES" contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="h1Q-FS-jlg" customClass="ArticleSearchBar" customModule="NetNewsWire" customModuleProvider="target">
|
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="h1Q-FS-jlg" customClass="ArticleSearchBar" customModule="NetNewsWire" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="777" width="414" height="36"/>
|
<rect key="frame" x="0.0" y="777" width="414" height="36"/>
|
||||||
<color key="backgroundColor" name="barBackgroundColor"/>
|
<color key="backgroundColor" name="barBackgroundColor"/>
|
||||||
</view>
|
</view>
|
||||||
@ -32,6 +33,9 @@
|
|||||||
</view>
|
</view>
|
||||||
<toolbarItems>
|
<toolbarItems>
|
||||||
<barButtonItem title="Toggle Read" image="circle" catalog="system" id="hy0-LS-MzE">
|
<barButtonItem title="Toggle Read" image="circle" catalog="system" id="hy0-LS-MzE">
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Button title: Toggle Read"/>
|
||||||
|
</attributedString>
|
||||||
<userDefinedRuntimeAttributes>
|
<userDefinedRuntimeAttributes>
|
||||||
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Toggle Read"/>
|
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Toggle Read"/>
|
||||||
</userDefinedRuntimeAttributes>
|
</userDefinedRuntimeAttributes>
|
||||||
@ -41,6 +45,9 @@
|
|||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="n8Q-7d-yb4"/>
|
<barButtonItem style="plain" systemItem="flexibleSpace" id="n8Q-7d-yb4"/>
|
||||||
<barButtonItem title="Toggle Starred" image="star" catalog="system" id="wU4-eH-wC9">
|
<barButtonItem title="Toggle Starred" image="star" catalog="system" id="wU4-eH-wC9">
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Button title: Toggle Starred"/>
|
||||||
|
</attributedString>
|
||||||
<userDefinedRuntimeAttributes>
|
<userDefinedRuntimeAttributes>
|
||||||
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Toggle Starred"/>
|
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Toggle Starred"/>
|
||||||
</userDefinedRuntimeAttributes>
|
</userDefinedRuntimeAttributes>
|
||||||
@ -50,6 +57,9 @@
|
|||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="bf9-AH-def"/>
|
<barButtonItem style="plain" systemItem="flexibleSpace" id="bf9-AH-def"/>
|
||||||
<barButtonItem enabled="NO" title="Next Unread" image="chevron.down.circle" catalog="system" id="2w5-e9-C2V">
|
<barButtonItem enabled="NO" title="Next Unread" image="chevron.down.circle" catalog="system" id="2w5-e9-C2V">
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Button title: Next Unread"/>
|
||||||
|
</attributedString>
|
||||||
<userDefinedRuntimeAttributes>
|
<userDefinedRuntimeAttributes>
|
||||||
<userDefinedRuntimeAttribute type="boolean" keyPath="accEnabled" value="YES"/>
|
<userDefinedRuntimeAttribute type="boolean" keyPath="accEnabled" value="YES"/>
|
||||||
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Next Unread"/>
|
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Next Unread"/>
|
||||||
@ -61,6 +71,9 @@
|
|||||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="vAq-iW-Yyo"/>
|
<barButtonItem style="plain" systemItem="flexibleSpace" id="vAq-iW-Yyo"/>
|
||||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="KUq-GZ-WY9"/>
|
<barButtonItem style="plain" systemItem="flexibleSpace" id="KUq-GZ-WY9"/>
|
||||||
<barButtonItem image="square.and.arrow.up" catalog="system" id="9Ut-5B-JKP">
|
<barButtonItem image="square.and.arrow.up" catalog="system" id="9Ut-5B-JKP">
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Button title: Share"/>
|
||||||
|
</attributedString>
|
||||||
<userDefinedRuntimeAttributes>
|
<userDefinedRuntimeAttributes>
|
||||||
<userDefinedRuntimeAttribute type="boolean" keyPath="accEnabled" value="YES"/>
|
<userDefinedRuntimeAttribute type="boolean" keyPath="accEnabled" value="YES"/>
|
||||||
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Share"/>
|
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Share"/>
|
||||||
@ -73,6 +86,9 @@
|
|||||||
<navigationItem key="navigationItem" largeTitleDisplayMode="never" id="mOI-FS-AaM">
|
<navigationItem key="navigationItem" largeTitleDisplayMode="never" id="mOI-FS-AaM">
|
||||||
<rightBarButtonItems>
|
<rightBarButtonItems>
|
||||||
<barButtonItem title="Next Article" image="chevron.down" catalog="system" id="2qz-M5-Yhk">
|
<barButtonItem title="Next Article" image="chevron.down" catalog="system" id="2qz-M5-Yhk">
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Button title: Next Article"/>
|
||||||
|
</attributedString>
|
||||||
<userDefinedRuntimeAttributes>
|
<userDefinedRuntimeAttributes>
|
||||||
<userDefinedRuntimeAttribute type="boolean" keyPath="accEnabled" value="YES"/>
|
<userDefinedRuntimeAttribute type="boolean" keyPath="accEnabled" value="YES"/>
|
||||||
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Next Article"/>
|
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Next Article"/>
|
||||||
@ -82,6 +98,9 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
<barButtonItem title="Previous Article" image="chevron.up" catalog="system" id="v4j-fq-23N">
|
<barButtonItem title="Previous Article" image="chevron.up" catalog="system" id="v4j-fq-23N">
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Button title: Previous Article"/>
|
||||||
|
</attributedString>
|
||||||
<userDefinedRuntimeAttributes>
|
<userDefinedRuntimeAttributes>
|
||||||
<userDefinedRuntimeAttribute type="boolean" keyPath="accEnabled" value="YES"/>
|
<userDefinedRuntimeAttribute type="boolean" keyPath="accEnabled" value="YES"/>
|
||||||
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Previous Article"/>
|
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Previous Article"/>
|
||||||
@ -90,12 +109,21 @@
|
|||||||
<action selector="prevArticle:" destination="JEX-9P-axG" id="cMZ-tk-I4W"/>
|
<action selector="prevArticle:" destination="JEX-9P-axG" id="cMZ-tk-I4W"/>
|
||||||
</connections>
|
</connections>
|
||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
|
<barButtonItem image="ellipsis.circle" catalog="system" id="SoN-ax-tEE">
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Button Title: Appearance"/>
|
||||||
|
</attributedString>
|
||||||
|
<userDefinedRuntimeAttributes>
|
||||||
|
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Appearance"/>
|
||||||
|
</userDefinedRuntimeAttributes>
|
||||||
|
</barButtonItem>
|
||||||
</rightBarButtonItems>
|
</rightBarButtonItems>
|
||||||
</navigationItem>
|
</navigationItem>
|
||||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
|
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
|
||||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="actionBarButtonItem" destination="9Ut-5B-JKP" id="9bO-kz-cTz"/>
|
<outlet property="actionBarButtonItem" destination="9Ut-5B-JKP" id="9bO-kz-cTz"/>
|
||||||
|
<outlet property="appearanceBarButtonItem" destination="SoN-ax-tEE" id="UZr-ut-0fn"/>
|
||||||
<outlet property="nextArticleBarButtonItem" destination="2qz-M5-Yhk" id="IQd-jx-qEr"/>
|
<outlet property="nextArticleBarButtonItem" destination="2qz-M5-Yhk" id="IQd-jx-qEr"/>
|
||||||
<outlet property="nextUnreadBarButtonItem" destination="2w5-e9-C2V" id="Ekf-My-AHN"/>
|
<outlet property="nextUnreadBarButtonItem" destination="2w5-e9-C2V" id="Ekf-My-AHN"/>
|
||||||
<outlet property="prevArticleBarButtonItem" destination="v4j-fq-23N" id="Gny-Oh-cQa"/>
|
<outlet property="prevArticleBarButtonItem" destination="v4j-fq-23N" id="Gny-Oh-cQa"/>
|
||||||
@ -107,18 +135,18 @@
|
|||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="FJe-Yq-33r" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="FJe-Yq-33r" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="2318.840579710145" y="-759.375"/>
|
<point key="canvasLocation" x="451" y="-431"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Timeline-->
|
<!--Timeline-->
|
||||||
<scene sceneID="fag-XH-avP">
|
<scene sceneID="fag-XH-avP">
|
||||||
<objects>
|
<objects>
|
||||||
<tableViewController storyboardIdentifier="TimelineViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" clearsSelectionOnViewWillAppear="NO" id="Kyk-vK-QRX" customClass="TimelineViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
<tableViewController storyboardIdentifier="MasterTimelineViewController" extendedLayoutIncludesOpaqueBars="YES" useStoryboardIdentifierAsRestorationIdentifier="YES" clearsSelectionOnViewWillAppear="NO" id="Kyk-vK-QRX" customClass="MasterTimelineViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="onDrag" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="mtv-Ik-FoJ">
|
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="onDrag" dataMode="prototypes" style="plain" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="mtv-Ik-FoJ">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="721"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" rowHeight="208" id="T5d-L4-OKG" customClass="TimelineTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" rowHeight="208" id="T5d-L4-OKG" customClass="MasterTimelineTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="50" width="414" height="208"/>
|
<rect key="frame" x="0.0" y="50" width="414" height="208"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="T5d-L4-OKG" id="QKC-jN-XDy">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="T5d-L4-OKG" id="QKC-jN-XDy">
|
||||||
@ -133,7 +161,10 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</tableView>
|
</tableView>
|
||||||
<toolbarItems>
|
<toolbarItems>
|
||||||
<barButtonItem title="Item" image="markAllAsRead" id="fTv-eX-72r">
|
<barButtonItem title="Item" image="arrow.up.arrow.down.circle" catalog="system" id="fTv-eX-72r">
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Mark All as Read button title."/>
|
||||||
|
</attributedString>
|
||||||
<userDefinedRuntimeAttributes>
|
<userDefinedRuntimeAttributes>
|
||||||
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Mark All as Read"/>
|
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Mark All as Read"/>
|
||||||
</userDefinedRuntimeAttributes>
|
</userDefinedRuntimeAttributes>
|
||||||
@ -144,7 +175,11 @@
|
|||||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="53V-wq-bat"/>
|
<barButtonItem style="plain" systemItem="flexibleSpace" id="53V-wq-bat"/>
|
||||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="93y-8j-WBh"/>
|
<barButtonItem style="plain" systemItem="flexibleSpace" id="93y-8j-WBh"/>
|
||||||
</toolbarItems>
|
</toolbarItems>
|
||||||
<navigationItem key="navigationItem" title="Timeline" largeTitleDisplayMode="never" id="wcC-1L-ug4"/>
|
<navigationItem key="navigationItem" title="Timeline" largeTitleDisplayMode="never" customizationIdentifier="" id="wcC-1L-ug4">
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Title used in the navigation bar for the Timeline."/>
|
||||||
|
</attributedString>
|
||||||
|
</navigationItem>
|
||||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" translucent="NO" prompted="NO"/>
|
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" translucent="NO" prompted="NO"/>
|
||||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics" translucent="NO"/>
|
<simulatedToolbarMetrics key="simulatedBottomBarMetrics" translucent="NO"/>
|
||||||
<connections>
|
<connections>
|
||||||
@ -153,18 +188,32 @@
|
|||||||
</tableViewController>
|
</tableViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="nzm-Gf-Xce" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="nzm-Gf-Xce" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="1620" y="-759"/>
|
<point key="canvasLocation" x="451" y="-1124"/>
|
||||||
|
</scene>
|
||||||
|
<!--Root Split View Controller-->
|
||||||
|
<scene sceneID="FfI-oe-67h">
|
||||||
|
<objects>
|
||||||
|
<splitViewController storyboardIdentifier="RootSplitViewController" allowDoubleColumnStyle="YES" preferredDisplayMode="twoBeside" id="AJQ-jq-uMa" customClass="RootSplitViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<connections>
|
||||||
|
<segue destination="Kyk-vK-QRX" kind="relationship" relationship="supplementaryViewController" id="FW6-KM-3C4"/>
|
||||||
|
<segue destination="JEX-9P-axG" kind="relationship" relationship="detailViewController" id="JbU-kn-u7r"/>
|
||||||
|
<segue destination="7bK-jq-Zjz" kind="relationship" relationship="masterViewController" id="rFx-mT-r7a"/>
|
||||||
|
</connections>
|
||||||
|
</splitViewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="9SW-km-PuE" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-1320" y="-1123"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Feeds-->
|
<!--Feeds-->
|
||||||
<scene sceneID="smW-Zh-WAh">
|
<scene sceneID="smW-Zh-WAh">
|
||||||
<objects>
|
<objects>
|
||||||
<tableViewController storyboardIdentifier="FeedsViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" clearsSelectionOnViewWillAppear="NO" id="7bK-jq-Zjz" customClass="FeedsViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
<tableViewController storyboardIdentifier="MasterFeedViewController" extendedLayoutIncludesOpaqueBars="YES" useStoryboardIdentifierAsRestorationIdentifier="YES" clearsSelectionOnViewWillAppear="NO" id="7bK-jq-Zjz" customClass="MasterFeedViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="r7i-6Z-zg0">
|
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="r7i-6Z-zg0">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="804"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="zNG-5C-pQm" customClass="FeedTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="zNG-5C-pQm" customClass="MasterFeedTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="55.5" width="414" height="43.5"/>
|
<rect key="frame" x="0.0" y="55.5" width="414" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="zNG-5C-pQm" id="5gB-Jr-qIo">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="zNG-5C-pQm" id="5gB-Jr-qIo">
|
||||||
@ -182,6 +231,9 @@
|
|||||||
</tableView>
|
</tableView>
|
||||||
<toolbarItems>
|
<toolbarItems>
|
||||||
<barButtonItem title="Settings" image="gear" catalog="system" id="TlU-Pg-ATe">
|
<barButtonItem title="Settings" image="gear" catalog="system" id="TlU-Pg-ATe">
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Settings bar button item."/>
|
||||||
|
</attributedString>
|
||||||
<userDefinedRuntimeAttributes>
|
<userDefinedRuntimeAttributes>
|
||||||
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Settings"/>
|
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Settings"/>
|
||||||
</userDefinedRuntimeAttributes>
|
</userDefinedRuntimeAttributes>
|
||||||
@ -192,13 +244,19 @@
|
|||||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="Rbh-Vg-Wo8"/>
|
<barButtonItem style="plain" systemItem="flexibleSpace" id="Rbh-Vg-Wo8"/>
|
||||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="Vhj-bc-20A"/>
|
<barButtonItem style="plain" systemItem="flexibleSpace" id="Vhj-bc-20A"/>
|
||||||
<barButtonItem systemItem="add" id="YFE-wd-vFC">
|
<barButtonItem systemItem="add" id="YFE-wd-vFC">
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Add New Item bar button item."/>
|
||||||
|
</attributedString>
|
||||||
<userDefinedRuntimeAttributes>
|
<userDefinedRuntimeAttributes>
|
||||||
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Add Item"/>
|
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Add Item"/>
|
||||||
</userDefinedRuntimeAttributes>
|
</userDefinedRuntimeAttributes>
|
||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
</toolbarItems>
|
</toolbarItems>
|
||||||
<navigationItem key="navigationItem" title="Feeds" id="Zdf-7t-Un8">
|
<navigationItem key="navigationItem" title="Feeds" customizationIdentifier="navigationbar.feeds.title" id="Zdf-7t-Un8">
|
||||||
<barButtonItem key="rightBarButtonItem" image="line.3.horizontal.decrease.circle" catalog="system" id="9ro-XY-5xU">
|
<barButtonItem key="rightBarButtonItem" image="line.3.horizontal.decrease.circle" catalog="system" id="9ro-XY-5xU">
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Feeds Filter bar button item."/>
|
||||||
|
</attributedString>
|
||||||
<userDefinedRuntimeAttributes>
|
<userDefinedRuntimeAttributes>
|
||||||
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Feeds Filter"/>
|
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Feeds Filter"/>
|
||||||
</userDefinedRuntimeAttributes>
|
</userDefinedRuntimeAttributes>
|
||||||
@ -206,9 +264,15 @@
|
|||||||
<action selector="toggleFilter:" destination="7bK-jq-Zjz" id="jmL-ei-avl"/>
|
<action selector="toggleFilter:" destination="7bK-jq-Zjz" id="jmL-ei-avl"/>
|
||||||
</connections>
|
</connections>
|
||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Title used in the navigation bar for the Feeds list."/>
|
||||||
|
</attributedString>
|
||||||
</navigationItem>
|
</navigationItem>
|
||||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
|
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
|
||||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="Feeds scene."/>
|
||||||
|
</attributedString>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="addNewItemButton" destination="YFE-wd-vFC" id="NMJ-uE-zGh"/>
|
<outlet property="addNewItemButton" destination="YFE-wd-vFC" id="NMJ-uE-zGh"/>
|
||||||
<outlet property="filterButton" destination="9ro-XY-5xU" id="PSL-lE-ITK"/>
|
<outlet property="filterButton" destination="9ro-XY-5xU" id="PSL-lE-ITK"/>
|
||||||
@ -216,7 +280,7 @@
|
|||||||
</tableViewController>
|
</tableViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Rux-fX-hf1" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="Rux-fX-hf1" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="900" y="-759"/>
|
<point key="canvasLocation" x="452" y="-1794"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Image View Controller-->
|
<!--Image View Controller-->
|
||||||
<scene sceneID="TT4-oA-DBw">
|
<scene sceneID="TT4-oA-DBw">
|
||||||
@ -316,28 +380,40 @@
|
|||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Blog Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YsT-Lt-Zry">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Blog Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YsT-Lt-Zry">
|
||||||
<rect key="frame" x="20" y="8" width="87" height="20.5"/>
|
<rect key="frame" x="20" y="8" width="86.5" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
|
||||||
<color key="textColor" name="primaryAccentColor"/>
|
<color key="textColor" name="primaryAccentColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="This does not need to be localized as it is provided by feed data."/>
|
||||||
|
</attributedString>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Blog Author" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7GV-PV-YVq">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Blog Author" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7GV-PV-YVq">
|
||||||
<rect key="frame" x="20" y="36.5" width="90" height="21"/>
|
<rect key="frame" x="20" y="36.5" width="90" height="21"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="This does not need to be localized as it is provided by feed data."/>
|
||||||
|
</attributedString>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Article Title" textAlignment="natural" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iFp-rn-HhQ">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Article Title" textAlignment="natural" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iFp-rn-HhQ">
|
||||||
<rect key="frame" x="20" y="74.5" width="136" height="33.5"/>
|
<rect key="frame" x="20" y="74.5" width="136" height="33.5"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle1"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle1"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="This does not need to be localized as it is provided by feed data."/>
|
||||||
|
</attributedString>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0Hz-Dv-MhU">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0Hz-Dv-MhU">
|
||||||
<rect key="frame" x="20" y="116" width="44" height="20.5"/>
|
<rect key="frame" x="20" y="116" width="43.5" height="20.5"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
|
||||||
<nil key="textColor"/>
|
<nil key="textColor"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
|
<attributedString key="userComments">
|
||||||
|
<fragment content="This does not need to be localized as it is provided by feed data."/>
|
||||||
|
</attributedString>
|
||||||
</label>
|
</label>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9Ms-dt-2M8">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9Ms-dt-2M8">
|
||||||
<rect key="frame" x="346" y="8" width="48" height="48"/>
|
<rect key="frame" x="346" y="8" width="48" height="48"/>
|
||||||
@ -396,15 +472,16 @@
|
|||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
|
<image name="arrow.up.arrow.down.circle" catalog="system" width="128" height="123"/>
|
||||||
<image name="chevron.down" catalog="system" width="128" height="70"/>
|
<image name="chevron.down" catalog="system" width="128" height="70"/>
|
||||||
<image name="chevron.down.circle" catalog="system" width="128" height="123"/>
|
<image name="chevron.down.circle" catalog="system" width="128" height="123"/>
|
||||||
<image name="chevron.up" catalog="system" width="128" height="70"/>
|
<image name="chevron.up" catalog="system" width="128" height="70"/>
|
||||||
<image name="circle" catalog="system" width="128" height="123"/>
|
<image name="circle" catalog="system" width="128" height="123"/>
|
||||||
|
<image name="ellipsis.circle" catalog="system" width="128" height="123"/>
|
||||||
<image name="gear" catalog="system" width="128" height="122"/>
|
<image name="gear" catalog="system" width="128" height="122"/>
|
||||||
<image name="line.3.horizontal.decrease.circle" catalog="system" width="128" height="123"/>
|
<image name="line.3.horizontal.decrease.circle" catalog="system" width="128" height="123"/>
|
||||||
<image name="markAllAsRead" width="13" height="20"/>
|
|
||||||
<image name="multiply.circle.fill" catalog="system" width="128" height="123"/>
|
<image name="multiply.circle.fill" catalog="system" width="128" height="123"/>
|
||||||
<image name="square.and.arrow.up" catalog="system" width="108" height="128"/>
|
<image name="square.and.arrow.up" catalog="system" width="115" height="128"/>
|
||||||
<image name="square.and.arrow.up.fill" catalog="system" width="115" height="128"/>
|
<image name="square.and.arrow.up.fill" catalog="system" width="115" height="128"/>
|
||||||
<image name="star" catalog="system" width="128" height="116"/>
|
<image name="star" catalog="system" width="128" height="116"/>
|
||||||
<namedColor name="barBackgroundColor">
|
<namedColor name="barBackgroundColor">
|
||||||
|
@ -11,7 +11,7 @@ import MobileCoreServices
|
|||||||
import Account
|
import Account
|
||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
extension FeedsViewController: UITableViewDragDelegate {
|
extension SidebarViewController: UITableViewDragDelegate {
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
||||||
guard let node = coordinator.nodeFor(indexPath), let feed = node.representedObject as? Feed else {
|
guard let node = coordinator.nodeFor(indexPath), let feed = node.representedObject as? Feed else {
|
||||||
|
@ -11,7 +11,7 @@ import RSCore
|
|||||||
import Account
|
import Account
|
||||||
import RSTree
|
import RSTree
|
||||||
|
|
||||||
extension FeedsViewController: UITableViewDropDelegate {
|
extension SidebarViewController: UITableViewDropDelegate {
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, canHandle session: UIDropSession) -> Bool {
|
func tableView(_ tableView: UITableView, canHandle session: UIDropSession) -> Bool {
|
||||||
return session.localDragSession != nil
|
return session.localDragSession != nil
|
||||||
|
@ -25,6 +25,7 @@ struct ShadowTableChanges {
|
|||||||
var section: Int
|
var section: Int
|
||||||
var deletes: Set<Int>?
|
var deletes: Set<Int>?
|
||||||
var inserts: Set<Int>?
|
var inserts: Set<Int>?
|
||||||
|
var reloads: Set<Int>?
|
||||||
var moves: Set<ShadowTableChanges.Move>?
|
var moves: Set<ShadowTableChanges.Move>?
|
||||||
|
|
||||||
var isEmpty: Bool {
|
var isEmpty: Bool {
|
||||||
@ -41,18 +42,23 @@ struct ShadowTableChanges {
|
|||||||
return inserts.map { IndexPath(row: $0, section: section) }
|
return inserts.map { IndexPath(row: $0, section: section) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var reloadIndexPaths: [IndexPath]? {
|
||||||
|
guard let reloads = reloads else { return nil }
|
||||||
|
return reloads.map { IndexPath(row: $0, section: section) }
|
||||||
|
}
|
||||||
|
|
||||||
var moveIndexPaths: [(IndexPath, IndexPath)]? {
|
var moveIndexPaths: [(IndexPath, IndexPath)]? {
|
||||||
guard let moves = moves else { return nil }
|
guard let moves = moves else { return nil }
|
||||||
return moves.map { (IndexPath(row: $0.from, section: section), IndexPath(row: $0.to, section: section)) }
|
return moves.map { (IndexPath(row: $0.from, section: section), IndexPath(row: $0.to, section: section)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
init(section: Int, deletes: Set<Int>?, inserts: Set<Int>?, moves: Set<Move>?) {
|
init(section: Int, deletes: Set<Int>?, inserts: Set<Int>?, reloads: Set<Int>?, moves: Set<Move>?) {
|
||||||
self.section = section
|
self.section = section
|
||||||
self.deletes = deletes
|
self.deletes = deletes
|
||||||
self.inserts = inserts
|
self.inserts = inserts
|
||||||
|
self.reloads = reloads
|
||||||
self.moves = moves
|
self.moves = moves
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var deletes: Set<Int>?
|
var deletes: Set<Int>?
|
||||||
|
@ -13,7 +13,7 @@ import RSCore
|
|||||||
import RSTree
|
import RSTree
|
||||||
import SafariServices
|
import SafariServices
|
||||||
|
|
||||||
class FeedsViewController: UITableViewController, UndoableCommandRunner {
|
class SidebarViewController: UITableViewController, UndoableCommandRunner {
|
||||||
|
|
||||||
@IBOutlet weak var filterButton: UIBarButtonItem!
|
@IBOutlet weak var filterButton: UIBarButtonItem!
|
||||||
private var refreshProgressView: RefreshProgressView?
|
private var refreshProgressView: RefreshProgressView?
|
||||||
@ -628,7 +628,7 @@ class FeedsViewController: UITableViewController, UndoableCommandRunner {
|
|||||||
|
|
||||||
// MARK: UIContextMenuInteractionDelegate
|
// MARK: UIContextMenuInteractionDelegate
|
||||||
|
|
||||||
extension FeedsViewController: UIContextMenuInteractionDelegate {
|
extension SidebarViewController: UIContextMenuInteractionDelegate {
|
||||||
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
|
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
|
||||||
|
|
||||||
guard let sectionIndex = interaction.view?.tag,
|
guard let sectionIndex = interaction.view?.tag,
|
||||||
@ -666,7 +666,7 @@ extension FeedsViewController: UIContextMenuInteractionDelegate {
|
|||||||
|
|
||||||
// MARK: FeedTableViewSectionHeaderDelegate
|
// MARK: FeedTableViewSectionHeaderDelegate
|
||||||
|
|
||||||
extension FeedsViewController: FeedTableViewSectionHeaderDelegate {
|
extension SidebarViewController: FeedTableViewSectionHeaderDelegate {
|
||||||
|
|
||||||
func FeedTableViewSectionHeaderDisclosureDidToggle(_ sender: FeedTableViewSectionHeader) {
|
func FeedTableViewSectionHeaderDisclosureDidToggle(_ sender: FeedTableViewSectionHeader) {
|
||||||
toggle(sender)
|
toggle(sender)
|
||||||
@ -676,7 +676,7 @@ extension FeedsViewController: FeedTableViewSectionHeaderDelegate {
|
|||||||
|
|
||||||
// MARK: TableViewCellDelegate
|
// MARK: TableViewCellDelegate
|
||||||
|
|
||||||
extension FeedsViewController: FeedTableViewCellDelegate {
|
extension SidebarViewController: FeedTableViewCellDelegate {
|
||||||
|
|
||||||
func feedTableViewCellDisclosureDidToggle(_ sender: FeedTableViewCell, expanding: Bool) {
|
func feedTableViewCellDisclosureDidToggle(_ sender: FeedTableViewCell, expanding: Bool) {
|
||||||
if expanding {
|
if expanding {
|
||||||
@ -690,7 +690,7 @@ extension FeedsViewController: FeedTableViewCellDelegate {
|
|||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private extension FeedsViewController {
|
private extension SidebarViewController {
|
||||||
|
|
||||||
func configureToolbar() {
|
func configureToolbar() {
|
||||||
guard let refreshProgressView = Bundle.main.loadNibNamed("RefreshProgressView", owner: self, options: nil)?[0] as? RefreshProgressView else {
|
guard let refreshProgressView = Bundle.main.loadNibNamed("RefreshProgressView", owner: self, options: nil)?[0] as? RefreshProgressView else {
|
||||||
@ -1230,7 +1230,7 @@ private extension FeedsViewController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FeedsViewController: UIGestureRecognizerDelegate {
|
extension SidebarViewController: UIGestureRecognizerDelegate {
|
||||||
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
guard let gestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer else {
|
guard let gestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer else {
|
||||||
return false
|
return false
|
@ -9,8 +9,8 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import Account
|
import Account
|
||||||
|
|
||||||
class RootSplitViewController: UISplitViewController {
|
final class RootSplitViewController: UISplitViewController {
|
||||||
|
|
||||||
var coordinator: SceneCoordinator!
|
var coordinator: SceneCoordinator!
|
||||||
|
|
||||||
override var prefersStatusBarHidden: Bool {
|
override var prefersStatusBarHidden: Bool {
|
||||||
@ -25,11 +25,11 @@ class RootSplitViewController: UISplitViewController {
|
|||||||
coordinator.resetFocus()
|
coordinator.resetFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
override func show(_ column: UISplitViewController.Column) {
|
||||||
self.coordinator.configurePanelMode(for: size)
|
guard !coordinator.isNavigationDisabled else { return }
|
||||||
super.viewWillTransition(to: size, with: coordinator)
|
super.show(column)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Keyboard Shortcuts
|
// MARK: Keyboard Shortcuts
|
||||||
|
|
||||||
@objc func scrollOrGoToNextUnread(_ sender: Any?) {
|
@objc func scrollOrGoToNextUnread(_ sender: Any?) {
|
||||||
|
@ -13,11 +13,17 @@ import Articles
|
|||||||
import RSCore
|
import RSCore
|
||||||
import RSTree
|
import RSTree
|
||||||
import SafariServices
|
import SafariServices
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
enum PanelMode {
|
protocol MainControllerIdentifiable {
|
||||||
case unset
|
var mainControllerIdentifier: MainControllerIdentifier { get }
|
||||||
case three
|
}
|
||||||
case standard
|
|
||||||
|
enum MainControllerIdentifier {
|
||||||
|
case none
|
||||||
|
case mainFeed
|
||||||
|
case mainTimeline
|
||||||
|
case article
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SearchScope: Int {
|
enum SearchScope: Int {
|
||||||
@ -52,41 +58,30 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
return rootSplitViewController.undoManager
|
return rootSplitViewController.undoManager
|
||||||
}
|
}
|
||||||
|
|
||||||
private var panelMode: PanelMode = .unset
|
|
||||||
|
|
||||||
private var activityManager = ActivityManager()
|
private var activityManager = ActivityManager()
|
||||||
|
|
||||||
private var rootSplitViewController: RootSplitViewController!
|
private var rootSplitViewController: RootSplitViewController!
|
||||||
private var navigationController: UINavigationController!
|
private var sidebarViewController: SidebarViewController!
|
||||||
private var feedsViewController: FeedsViewController!
|
|
||||||
private var timelineViewController: TimelineViewController?
|
private var timelineViewController: TimelineViewController?
|
||||||
private var subSplitViewController: UISplitViewController?
|
private var articleViewController: ArticleViewController?
|
||||||
|
|
||||||
private var articleViewController: ArticleViewController? {
|
private var lastMainControllerToAppear = MainControllerIdentifier.none
|
||||||
if let detail = navigationController.viewControllers.last as? ArticleViewController {
|
|
||||||
return detail
|
|
||||||
}
|
|
||||||
if let subSplit = subSplitViewController {
|
|
||||||
if let navController = subSplit.viewControllers.last as? UINavigationController {
|
|
||||||
return navController.topViewController as? ArticleViewController
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if let navController = rootSplitViewController.viewControllers.last as? UINavigationController {
|
|
||||||
return navController.topViewController as? ArticleViewController
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
private var wasRootSplitViewControllerCollapsed = false
|
|
||||||
|
|
||||||
private let fetchAndMergeArticlesQueue = CoalescingQueue(name: "Fetch and Merge Articles", interval: 0.5)
|
private let fetchAndMergeArticlesQueue = CoalescingQueue(name: "Fetch and Merge Articles", interval: 0.5)
|
||||||
private let rebuildBackingStoresQueue = CoalescingQueue(name: "Rebuild The Backing Stores", interval: 0.5)
|
private let rebuildBackingStoresQueue = CoalescingQueue(name: "Rebuild The Backing Stores", interval: 0.5)
|
||||||
private var fetchSerialNumber = 0
|
private var fetchSerialNumber = 0
|
||||||
private let fetchRequestQueue = FetchRequestQueue()
|
private let fetchRequestQueue = FetchRequestQueue()
|
||||||
|
|
||||||
|
/// Which Containers are expanded
|
||||||
private var expandedTable = Set<ContainerIdentifier>()
|
private var expandedTable = Set<ContainerIdentifier>()
|
||||||
|
|
||||||
|
/// Which Containers used to be expanded. Reset by rebuilding the Shadow Table.
|
||||||
|
private var lastExpandedTable = Set<ContainerIdentifier>()
|
||||||
|
|
||||||
|
/// Which Feeds have the Read Articles Filter enabled
|
||||||
private var readFilterEnabledTable = [SidebarItemIdentifier: Bool]()
|
private var readFilterEnabledTable = [SidebarItemIdentifier: Bool]()
|
||||||
|
|
||||||
|
/// Flattened tree structure for the Sidebar
|
||||||
private var shadowTable = [(sectionID: String, feedNodes: [FeedNode])]()
|
private var shadowTable = [(sectionID: String, feedNodes: [FeedNode])]()
|
||||||
|
|
||||||
private(set) var preSearchTimelineFeed: SidebarItem?
|
private(set) var preSearchTimelineFeed: SidebarItem?
|
||||||
@ -95,10 +90,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
private var isSearching: Bool = false
|
private var isSearching: Bool = false
|
||||||
private var savedSearchArticles: ArticleArray? = nil
|
private var savedSearchArticles: ArticleArray? = nil
|
||||||
private var savedSearchArticleIds: Set<String>? = nil
|
private var savedSearchArticleIds: Set<String>? = nil
|
||||||
|
|
||||||
var isTimelineViewControllerPending = false
|
|
||||||
var isArticleViewControllerPending = false
|
|
||||||
|
|
||||||
private(set) var sortDirection = AppDefaults.shared.timelineSortDirection {
|
private(set) var sortDirection = AppDefaults.shared.timelineSortDirection {
|
||||||
didSet {
|
didSet {
|
||||||
if sortDirection != oldValue {
|
if sortDirection != oldValue {
|
||||||
@ -134,14 +126,12 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
return activity
|
return activity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isNavigationDisabled = false
|
||||||
|
|
||||||
var isRootSplitCollapsed: Bool {
|
var isRootSplitCollapsed: Bool {
|
||||||
return rootSplitViewController.isCollapsed
|
return rootSplitViewController.isCollapsed
|
||||||
}
|
}
|
||||||
|
|
||||||
var isThreePanelMode: Bool {
|
|
||||||
return panelMode == .three
|
|
||||||
}
|
|
||||||
|
|
||||||
var isReadFeedsFiltered: Bool {
|
var isReadFeedsFiltered: Bool {
|
||||||
return treeControllerDelegate.isReadFiltered
|
return treeControllerDelegate.isReadFiltered
|
||||||
}
|
}
|
||||||
@ -292,11 +282,32 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
|
|
||||||
var timelineUnreadCount: Int = 0
|
var timelineUnreadCount: Int = 0
|
||||||
|
|
||||||
override init() {
|
init(rootSplitViewController: RootSplitViewController) {
|
||||||
treeController = TreeController(delegate: treeControllerDelegate)
|
self.rootSplitViewController = rootSplitViewController
|
||||||
|
self.treeController = TreeController(delegate: treeControllerDelegate)
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.sidebarViewController = rootSplitViewController.viewController(for: .primary) as? SidebarViewController
|
||||||
|
self.sidebarViewController.coordinator = self
|
||||||
|
if let navController = self.sidebarViewController?.navigationController {
|
||||||
|
navController.delegate = self
|
||||||
|
configureNavigationController(navController)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.timelineViewController = rootSplitViewController.viewController(for: .supplementary) as? TimelineViewController
|
||||||
|
self.timelineViewController?.coordinator = self
|
||||||
|
if let navController = self.timelineViewController?.navigationController {
|
||||||
|
navController.delegate = self
|
||||||
|
configureNavigationController(navController)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.articleViewController = rootSplitViewController.viewController(for: .secondary) as? ArticleViewController
|
||||||
|
self.articleViewController?.coordinator = self
|
||||||
|
if let navController = self.articleViewController?.navigationController {
|
||||||
|
configureNavigationController(navController)
|
||||||
|
}
|
||||||
|
|
||||||
for sectionNode in treeController.rootNode.childNodes {
|
for sectionNode in treeController.rootNode.childNodes {
|
||||||
markExpanded(sectionNode)
|
markExpanded(sectionNode)
|
||||||
shadowTable.append((sectionID: "", feedNodes: [FeedNode]()))
|
shadowTable.append((sectionID: "", feedNodes: [FeedNode]()))
|
||||||
@ -319,30 +330,6 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDownloadDidFail(_:)), name: .didFailToImportThemeWithError, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(themeDownloadDidFail(_:)), name: .didFailToImportThemeWithError, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func start(for size: CGSize) -> UIViewController {
|
|
||||||
rootSplitViewController = RootSplitViewController()
|
|
||||||
rootSplitViewController.coordinator = self
|
|
||||||
rootSplitViewController.preferredDisplayMode = .oneBesideSecondary
|
|
||||||
rootSplitViewController.viewControllers = [InteractiveNavigationController.template()]
|
|
||||||
rootSplitViewController.delegate = self
|
|
||||||
|
|
||||||
navigationController = (rootSplitViewController.viewControllers.first as! UINavigationController)
|
|
||||||
navigationController.delegate = self
|
|
||||||
|
|
||||||
feedsViewController = UIStoryboard.main.instantiateController(ofType: FeedsViewController.self)
|
|
||||||
feedsViewController.coordinator = self
|
|
||||||
navigationController.pushViewController(feedsViewController, animated: false)
|
|
||||||
|
|
||||||
let articleViewController = UIStoryboard.main.instantiateController(ofType: ArticleViewController.self)
|
|
||||||
articleViewController.coordinator = self
|
|
||||||
let detailNavigationController = addNavControllerIfNecessary(articleViewController, showButton: true)
|
|
||||||
rootSplitViewController.showDetailViewController(detailNavigationController, sender: self)
|
|
||||||
|
|
||||||
configurePanelMode(for: size)
|
|
||||||
|
|
||||||
return rootSplitViewController
|
|
||||||
}
|
|
||||||
|
|
||||||
func restoreWindowState(_ activity: NSUserActivity?) {
|
func restoreWindowState(_ activity: NSUserActivity?) {
|
||||||
if let activity = activity, let windowState = activity.userInfo?[UserInfoKey.windowState] as? [AnyHashable: Any] {
|
if let activity = activity, let windowState = activity.userInfo?[UserInfoKey.windowState] as? [AnyHashable: Any] {
|
||||||
|
|
||||||
@ -359,6 +346,12 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if let isSidebarHidden = windowState[UserInfoKey.isSidebarHidden] as? Bool, isSidebarHidden {
|
||||||
|
// DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
||||||
|
// self.rootSplitViewController.preferredDisplayMode = .secondaryOnly
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
rebuildBackingStores(initialLoad: true)
|
rebuildBackingStores(initialLoad: true)
|
||||||
|
|
||||||
// You can't assign the Feeds Read Filter until we've built the backing stores at least once or there is nothing
|
// You can't assign the Feeds Read Filter until we've built the backing stores at least once or there is nothing
|
||||||
@ -397,31 +390,11 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
handleReadArticle(userInfo)
|
handleReadArticle(userInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func configurePanelMode(for size: CGSize) {
|
|
||||||
guard rootSplitViewController.traitCollection.userInterfaceIdiom == .pad else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size.width / size.height) > 1.2 {
|
|
||||||
if panelMode == .unset || panelMode == .standard {
|
|
||||||
panelMode = .three
|
|
||||||
configureThreePanelMode()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if panelMode == .unset || panelMode == .three {
|
|
||||||
panelMode = .standard
|
|
||||||
configureStandardPanelMode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wasRootSplitViewControllerCollapsed = rootSplitViewController.isCollapsed
|
|
||||||
}
|
|
||||||
|
|
||||||
func resetFocus() {
|
func resetFocus() {
|
||||||
if currentArticle != nil {
|
if currentArticle != nil {
|
||||||
timelineViewController?.focus()
|
timelineViewController?.focus()
|
||||||
} else {
|
} else {
|
||||||
feedsViewController?.focus()
|
sidebarViewController?.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,7 +409,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
|
|
||||||
func showSearch() {
|
func showSearch() {
|
||||||
selectFeed(indexPath: nil) {
|
selectFeed(indexPath: nil) {
|
||||||
self.installTimelineControllerIfNecessary(animated: false)
|
self.rootSplitViewController.show(.supplementary)
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now()) {
|
DispatchQueue.main.asyncAfter(deadline: .now()) {
|
||||||
self.timelineViewController!.showSearchAll()
|
self.timelineViewController!.showSearchAll()
|
||||||
}
|
}
|
||||||
@ -611,7 +584,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
treeControllerDelegate.isReadFiltered = true
|
treeControllerDelegate.isReadFiltered = true
|
||||||
}
|
}
|
||||||
rebuildBackingStores()
|
rebuildBackingStores()
|
||||||
feedsViewController?.updateUI()
|
sidebarViewController?.updateUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleReadArticlesFilter() {
|
func toggleReadArticlesFilter() {
|
||||||
@ -646,16 +619,33 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
return shadowTable[section].feedNodes.count
|
return shadowTable[section].feedNodes.count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nodeFor(_ section: Int) -> Node? {
|
||||||
|
return treeController.rootNode.childAtIndex(section)
|
||||||
|
}
|
||||||
|
|
||||||
func nodeFor(_ indexPath: IndexPath) -> Node? {
|
func nodeFor(_ indexPath: IndexPath) -> Node? {
|
||||||
guard indexPath.section < shadowTable.count && indexPath.row < shadowTable[indexPath.section].feedNodes.count else {
|
guard indexPath.section > -1 &&
|
||||||
|
indexPath.row > -1 &&
|
||||||
|
indexPath.section < shadowTable.count &&
|
||||||
|
indexPath.row < shadowTable[indexPath.section].feedNodes.count else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return shadowTable[indexPath.section].feedNodes[indexPath.row].node
|
return shadowTable[indexPath.section].feedNodes[indexPath.row].node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func indexPathFor(_ object: AnyObject) -> IndexPath? {
|
||||||
|
guard let node = treeController.rootNode.descendantNodeRepresentingObject(object) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return indexPathFor(node)
|
||||||
|
}
|
||||||
|
|
||||||
func indexPathFor(_ node: Node) -> IndexPath? {
|
func indexPathFor(_ node: Node) -> IndexPath? {
|
||||||
|
|
||||||
|
let feedNode = FeedNode(node)
|
||||||
|
|
||||||
for i in 0..<shadowTable.count {
|
for i in 0..<shadowTable.count {
|
||||||
if let row = shadowTable[i].feedNodes.firstIndex(of: FeedNode(node)) {
|
if let row = shadowTable[i].feedNodes.firstIndex(of: feedNode) {
|
||||||
return IndexPath(row: row, section: i)
|
return IndexPath(row: row, section: i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -717,8 +707,11 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
rebuildBackingStores()
|
rebuildBackingStores()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is a special function that expects the caller to change the disclosure arrow state outside this function.
|
||||||
|
/// Failure to do so will get the Sidebar into an invalid state.
|
||||||
func expand(_ node: Node) {
|
func expand(_ node: Node) {
|
||||||
guard let containerID = (node.representedObject as? ContainerIdentifiable)?.containerID else { return }
|
guard let containerID = (node.representedObject as? ContainerIdentifiable)?.containerID else { return }
|
||||||
|
lastExpandedTable.insert(containerID)
|
||||||
expand(containerID)
|
expand(containerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -740,8 +733,11 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
clearTimelineIfNoLongerAvailable()
|
clearTimelineIfNoLongerAvailable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is a special function that expects the caller to change the disclosure arrow state outside this function.
|
||||||
|
/// Failure to do so will get the Sidebar into an invalid state.
|
||||||
func collapse(_ node: Node) {
|
func collapse(_ node: Node) {
|
||||||
guard let containerID = (node.representedObject as? ContainerIdentifiable)?.containerID else { return }
|
guard let containerID = (node.representedObject as? ContainerIdentifiable)?.containerID else { return }
|
||||||
|
lastExpandedTable.remove(containerID)
|
||||||
collapse(containerID)
|
collapse(containerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -782,7 +778,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
currentFeedIndexPath = indexPath
|
currentFeedIndexPath = indexPath
|
||||||
feedsViewController.updateFeedSelection(animations: animations)
|
sidebarViewController.updateFeedSelection(animations: animations)
|
||||||
|
|
||||||
if deselectArticle {
|
if deselectArticle {
|
||||||
selectArticle(nil)
|
selectArticle(nil)
|
||||||
@ -791,7 +787,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
if let ip = indexPath, let node = nodeFor(ip), let feed = node.representedObject as? SidebarItem {
|
if let ip = indexPath, let node = nodeFor(ip), let feed = node.representedObject as? SidebarItem {
|
||||||
|
|
||||||
self.activityManager.selecting(feed: feed)
|
self.activityManager.selecting(feed: feed)
|
||||||
self.installTimelineControllerIfNecessary(animated: animations.contains(.navigation))
|
self.rootSplitViewController.show(.supplementary)
|
||||||
setTimelineFeed(feed, animated: false) {
|
setTimelineFeed(feed, animated: false) {
|
||||||
if self.isReadFeedsFiltered {
|
if self.isReadFeedsFiltered {
|
||||||
self.rebuildBackingStores()
|
self.rebuildBackingStores()
|
||||||
@ -806,14 +802,10 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
self.rebuildBackingStores()
|
self.rebuildBackingStores()
|
||||||
}
|
}
|
||||||
self.activityManager.invalidateSelecting()
|
self.activityManager.invalidateSelecting()
|
||||||
if self.rootSplitViewController.isCollapsed && self.navControllerForTimeline().viewControllers.last is TimelineViewController {
|
self.rootSplitViewController.show(.primary)
|
||||||
self.navControllerForTimeline().popViewController(animated: animations.contains(.navigation))
|
|
||||||
}
|
|
||||||
completion?()
|
completion?()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectPrevFeed() {
|
func selectPrevFeed() {
|
||||||
@ -856,31 +848,20 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
activityManager.reading(feed: timelineFeed, article: article)
|
activityManager.reading(feed: timelineFeed, article: article)
|
||||||
|
|
||||||
if article == nil {
|
if article == nil {
|
||||||
if rootSplitViewController.isCollapsed {
|
rootSplitViewController.show(.supplementary)
|
||||||
if navigationController.children.last is ArticleViewController {
|
|
||||||
navigationController.popViewController(animated: animations.contains(.navigation))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
articleViewController?.article = nil
|
|
||||||
}
|
|
||||||
timelineViewController?.updateArticleSelection(animations: animations)
|
timelineViewController?.updateArticleSelection(animations: animations)
|
||||||
|
articleViewController?.article = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentArticleViewController: ArticleViewController
|
rootSplitViewController.show(.secondary)
|
||||||
if articleViewController == nil {
|
|
||||||
currentArticleViewController = installArticleController(animated: animations.contains(.navigation))
|
|
||||||
} else {
|
|
||||||
currentArticleViewController = articleViewController!
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark article as read before navigating to it, so the read status does not flash unread/read on display
|
// Mark article as read before navigating to it, so the read status does not flash unread/read on display
|
||||||
markArticles(Set([article!]), statusKey: .read, flag: true)
|
markArticles(Set([article!]), statusKey: .read, flag: true)
|
||||||
|
|
||||||
timelineViewController?.updateArticleSelection(animations: animations)
|
timelineViewController?.updateArticleSelection(animations: animations)
|
||||||
currentArticleViewController.article = article
|
articleViewController?.article = article
|
||||||
if let isShowingExtractedArticle = isShowingExtractedArticle, let articleWindowScrollY = articleWindowScrollY {
|
if let isShowingExtractedArticle = isShowingExtractedArticle, let articleWindowScrollY = articleWindowScrollY {
|
||||||
currentArticleViewController.restoreScrollPosition = (isShowingExtractedArticle, articleWindowScrollY)
|
articleViewController?.restoreScrollPosition = (isShowingExtractedArticle, articleWindowScrollY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -897,8 +878,8 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
if let oldTimelineFeed = preSearchTimelineFeed {
|
if let oldTimelineFeed = preSearchTimelineFeed {
|
||||||
emptyTheTimeline()
|
emptyTheTimeline()
|
||||||
timelineFeed = oldTimelineFeed
|
timelineFeed = oldTimelineFeed
|
||||||
timelineViewController?.reinitializeArticles(resetScroll: true)
|
|
||||||
replaceArticles(with: savedSearchArticles!, animated: true)
|
replaceArticles(with: savedSearchArticles!, animated: true)
|
||||||
|
timelineViewController?.reinitializeArticles(resetScroll: true)
|
||||||
} else {
|
} else {
|
||||||
setTimelineFeed(nil, animated: true)
|
setTimelineFeed(nil, animated: true)
|
||||||
}
|
}
|
||||||
@ -972,11 +953,16 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
func selectPrevUnread() {
|
func selectPrevUnread() {
|
||||||
|
|
||||||
// This should never happen, but I don't want to risk throwing us
|
// This should never happen, but I don't want to risk throwing us
|
||||||
// into an infinate loop searching for an unread that isn't there.
|
// into an infinite loop searching for an unread that isn't there.
|
||||||
if appDelegate.unreadCount < 1 {
|
if appDelegate.unreadCount < 1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isNavigationDisabled = true
|
||||||
|
defer {
|
||||||
|
isNavigationDisabled = false
|
||||||
|
}
|
||||||
|
|
||||||
if selectPrevUnreadArticleInTimeline() {
|
if selectPrevUnreadArticleInTimeline() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -988,11 +974,16 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
func selectNextUnread() {
|
func selectNextUnread() {
|
||||||
|
|
||||||
// This should never happen, but I don't want to risk throwing us
|
// This should never happen, but I don't want to risk throwing us
|
||||||
// into an infinate loop searching for an unread that isn't there.
|
// into an infinite loop searching for an unread that isn't there.
|
||||||
if appDelegate.unreadCount < 1 {
|
if appDelegate.unreadCount < 1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isNavigationDisabled = true
|
||||||
|
defer {
|
||||||
|
isNavigationDisabled = false
|
||||||
|
}
|
||||||
|
|
||||||
if selectNextUnreadArticleInTimeline() {
|
if selectNextUnreadArticleInTimeline() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1027,7 +1018,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
|
|
||||||
func markAllAsReadInTimeline(completion: (() -> Void)? = nil) {
|
func markAllAsReadInTimeline(completion: (() -> Void)? = nil) {
|
||||||
markAllAsRead(articles) {
|
markAllAsRead(articles) {
|
||||||
self.navigationController.popViewController(animated: true)
|
self.rootSplitViewController.show(.primary)
|
||||||
completion?()
|
completion?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1109,7 +1100,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
return timelineFeed == feed
|
return timelineFeed == feed
|
||||||
}
|
}
|
||||||
|
|
||||||
func discloseFeed(_ feed: Feed, animations: Animations = [], completion: (() -> Void)? = nil) {
|
func discloseFeed(_ feed: Feed, initialLoad: Bool = false, animations: Animations = [], completion: (() -> Void)? = nil) {
|
||||||
if isSearching {
|
if isSearching {
|
||||||
timelineViewController?.hideSearch()
|
timelineViewController?.hideSearch()
|
||||||
}
|
}
|
||||||
@ -1133,7 +1124,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
self.treeControllerDelegate.addFilterException(parentFolderFeedID)
|
self.treeControllerDelegate.addFilterException(parentFolderFeedID)
|
||||||
}
|
}
|
||||||
|
|
||||||
rebuildBackingStores(completion: {
|
rebuildBackingStores(initialLoad: initialLoad, completion: {
|
||||||
self.treeControllerDelegate.resetFilterExceptions()
|
self.treeControllerDelegate.resetFilterExceptions()
|
||||||
self.selectFeed(feed, animations: animations, completion: completion)
|
self.selectFeed(feed, animations: animations, completion: completion)
|
||||||
})
|
})
|
||||||
@ -1162,7 +1153,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
settingsViewController.presentingParentController = rootSplitViewController
|
settingsViewController.presentingParentController = rootSplitViewController
|
||||||
rootSplitViewController.present(settingsNavController, animated: true)
|
rootSplitViewController.present(settingsNavController, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func showAccountInspector(for account: Account) {
|
func showAccountInspector(for account: Account) {
|
||||||
let accountInspectorNavController =
|
let accountInspectorNavController =
|
||||||
UIStoryboard.inspector.instantiateViewController(identifier: "AccountInspectorNavigationViewController") as! UINavigationController
|
UIStoryboard.inspector.instantiateViewController(identifier: "AccountInspectorNavigationViewController") as! UINavigationController
|
||||||
@ -1173,7 +1164,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
accountInspectorController.account = account
|
accountInspectorController.account = account
|
||||||
rootSplitViewController.present(accountInspectorNavController, animated: true)
|
rootSplitViewController.present(accountInspectorNavController, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func showFeedInspector() {
|
func showFeedInspector() {
|
||||||
let timelineFeed = timelineFeed as? Feed
|
let timelineFeed = timelineFeed as? Feed
|
||||||
let articleFeed = currentArticle?.feed
|
let articleFeed = currentArticle?.feed
|
||||||
@ -1192,7 +1183,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
feedInspectorController.feed = feed
|
feedInspectorController.feed = feed
|
||||||
rootSplitViewController.present(feedInspectorNavController, animated: true)
|
rootSplitViewController.present(feedInspectorNavController, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func showAddFeed(initialFeed: String? = nil, initialFeedName: String? = nil) {
|
func showAddFeed(initialFeed: String? = nil, initialFeedName: String? = nil) {
|
||||||
|
|
||||||
// Since Add Feed can be opened from anywhere with a keyboard shortcut, we have to deselect any currently selected feeds
|
// Since Add Feed can be opened from anywhere with a keyboard shortcut, we have to deselect any currently selected feeds
|
||||||
@ -1206,14 +1197,14 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
|
|
||||||
addNavViewController.modalPresentationStyle = .formSheet
|
addNavViewController.modalPresentationStyle = .formSheet
|
||||||
addNavViewController.preferredContentSize = AddFeedViewController.preferredContentSizeForFormSheetDisplay
|
addNavViewController.preferredContentSize = AddFeedViewController.preferredContentSizeForFormSheetDisplay
|
||||||
feedsViewController.present(addNavViewController, animated: true)
|
sidebarViewController.present(addNavViewController, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func showAddFolder() {
|
func showAddFolder() {
|
||||||
let addNavViewController = UIStoryboard.add.instantiateViewController(withIdentifier: "AddFolderViewControllerNav") as! UINavigationController
|
let addNavViewController = UIStoryboard.add.instantiateViewController(withIdentifier: "AddFolderViewControllerNav") as! UINavigationController
|
||||||
addNavViewController.modalPresentationStyle = .formSheet
|
addNavViewController.modalPresentationStyle = .formSheet
|
||||||
addNavViewController.preferredContentSize = AddFolderViewController.preferredContentSizeForFormSheetDisplay
|
addNavViewController.preferredContentSize = AddFolderViewController.preferredContentSizeForFormSheetDisplay
|
||||||
feedsViewController.present(addNavViewController, animated: true)
|
sidebarViewController.present(addNavViewController, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func showFullScreenImage(image: UIImage, imageTitle: String?, transitioningDelegate: UIViewControllerTransitioningDelegate) {
|
func showFullScreenImage(image: UIImage, imageTitle: String?, transitioningDelegate: UIViewControllerTransitioningDelegate) {
|
||||||
@ -1262,12 +1253,12 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
articleViewController?.openInAppBrowser()
|
articleViewController?.openInAppBrowser()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
feedsViewController.openInAppBrowser()
|
sidebarViewController.openInAppBrowser()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func navigateToFeeds() {
|
func navigateToFeeds() {
|
||||||
feedsViewController?.focus()
|
sidebarViewController?.focus()
|
||||||
selectArticle(nil)
|
selectArticle(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1309,75 +1300,74 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
|||||||
/// `SFSafariViewController` or `SettingsViewController`,
|
/// `SFSafariViewController` or `SettingsViewController`,
|
||||||
/// otherwise, this function does nothing.
|
/// otherwise, this function does nothing.
|
||||||
func dismissIfLaunchingFromExternalAction() {
|
func dismissIfLaunchingFromExternalAction() {
|
||||||
guard let presentedController = feedsViewController.presentedViewController else { return }
|
guard let presentedController = sidebarViewController.presentedViewController else { return }
|
||||||
|
|
||||||
if presentedController.isKind(of: SFSafariViewController.self) {
|
if presentedController.isKind(of: SFSafariViewController.self) {
|
||||||
presentedController.dismiss(animated: true, completion: nil)
|
presentedController.dismiss(animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
guard let settings = presentedController.children.first as? SettingsViewController else { return }
|
|
||||||
settings.dismiss(animated: true, completion: nil)
|
// There's no obvious way to detect if the presented controller
|
||||||
|
// is the SwiftUI UIHostingController<SettingsView>. Posting a notification
|
||||||
|
// which it can react to seems to be the simplest solution.
|
||||||
|
NotificationCenter.default.post(name: .LaunchedFromExternalAction, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: UISplitViewControllerDelegate
|
// MARK: UISplitViewControllerDelegate
|
||||||
|
|
||||||
extension SceneCoordinator: UISplitViewControllerDelegate {
|
extension SceneCoordinator: UISplitViewControllerDelegate {
|
||||||
|
|
||||||
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
|
|
||||||
timelineViewController?.updateUI()
|
|
||||||
|
|
||||||
guard !isThreePanelMode else {
|
func splitViewController(_ svc: UISplitViewController, topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column) -> UISplitViewController.Column {
|
||||||
return true
|
switch proposedTopColumn {
|
||||||
}
|
case .supplementary:
|
||||||
|
if currentFeedIndexPath != nil {
|
||||||
if let articleViewController = (secondaryViewController as? UINavigationController)?.topViewController as? ArticleViewController {
|
return .supplementary
|
||||||
if currentArticle != nil {
|
} else {
|
||||||
navigationController.pushViewController(articleViewController, animated: false)
|
return .primary
|
||||||
}
|
}
|
||||||
|
case .secondary:
|
||||||
|
if currentArticle != nil {
|
||||||
|
return .secondary
|
||||||
|
} else {
|
||||||
|
if currentFeedIndexPath != nil {
|
||||||
|
return .supplementary
|
||||||
|
} else {
|
||||||
|
return .primary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return .primary
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitViewController(_ splitViewController: UISplitViewController, separateSecondaryFrom primaryViewController: UIViewController) -> UIViewController? {
|
|
||||||
timelineViewController?.updateUI()
|
|
||||||
|
|
||||||
guard !isThreePanelMode else {
|
func splitViewController(_ svc: UISplitViewController, willChangeTo displayMode: UISplitViewController.DisplayMode) {
|
||||||
return subSplitViewController
|
articleViewController?.splitViewControllerWillChangeTo(displayMode: displayMode)
|
||||||
}
|
|
||||||
|
|
||||||
if let articleViewController = navigationController.viewControllers.last as? ArticleViewController {
|
|
||||||
articleViewController.showBars(self)
|
|
||||||
navigationController.popViewController(animated: false)
|
|
||||||
let controller = addNavControllerIfNecessary(articleViewController, showButton: true)
|
|
||||||
return controller
|
|
||||||
}
|
|
||||||
|
|
||||||
if currentArticle == nil {
|
|
||||||
let articleViewController = UIStoryboard.main.instantiateController(ofType: ArticleViewController.self)
|
|
||||||
articleViewController.coordinator = self
|
|
||||||
let controller = addNavControllerIfNecessary(articleViewController, showButton: true)
|
|
||||||
return controller
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: UINavigationControllerDelegate
|
// MARK: UINavigationControllerDelegate
|
||||||
|
|
||||||
extension SceneCoordinator: UINavigationControllerDelegate {
|
extension SceneCoordinator: UINavigationControllerDelegate {
|
||||||
|
|
||||||
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
|
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
|
||||||
|
guard UIApplication.shared.applicationState != .background else {
|
||||||
if UIApplication.shared.applicationState == .background {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guard rootSplitViewController.isCollapsed else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer {
|
||||||
|
if let mainController = viewController as? MainControllerIdentifiable {
|
||||||
|
lastMainControllerToAppear = mainController.mainControllerIdentifier
|
||||||
|
} else if let mainController = (viewController as? UINavigationController)?.topViewController as? MainControllerIdentifiable {
|
||||||
|
lastMainControllerToAppear = mainController.mainControllerIdentifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we are showing the Feeds and only the feeds start clearing stuff
|
// If we are showing the Feeds and only the feeds start clearing stuff
|
||||||
if viewController === feedsViewController && !isThreePanelMode && !isTimelineViewControllerPending {
|
if viewController === sidebarViewController && lastMainControllerToAppear == .mainTimeline {
|
||||||
activityManager.invalidateCurrentActivities()
|
activityManager.invalidateCurrentActivities()
|
||||||
selectFeed(nil, animations: [.scroll, .select, .navigation])
|
selectFeed(nil, animations: [.scroll, .select, .navigation])
|
||||||
return
|
return
|
||||||
@ -1387,26 +1377,52 @@ extension SceneCoordinator: UINavigationControllerDelegate {
|
|||||||
// Don't clear it if we have pushed an ArticleViewController, but don't yet see it on the navigation stack.
|
// Don't clear it if we have pushed an ArticleViewController, but don't yet see it on the navigation stack.
|
||||||
// This happens when we are going to the next unread and we need to grab another timeline to continue. The
|
// This happens when we are going to the next unread and we need to grab another timeline to continue. The
|
||||||
// ArticleViewController will be pushed, but we will briefly show the Timeline. Don't clear things out when that happens.
|
// ArticleViewController will be pushed, but we will briefly show the Timeline. Don't clear things out when that happens.
|
||||||
if viewController === timelineViewController && !isThreePanelMode && rootSplitViewController.isCollapsed && !isArticleViewControllerPending {
|
if viewController === timelineViewController && lastMainControllerToAppear == .article {
|
||||||
currentArticle = nil
|
selectArticle(nil, animations: [.scroll, .select, .navigation])
|
||||||
timelineViewController?.updateArticleSelection(animations: [.scroll, .select, .navigation])
|
|
||||||
activityManager.invalidateReading()
|
|
||||||
|
|
||||||
// Restore any bars hidden by the article controller
|
// Restore any bars hidden by the article controller
|
||||||
showStatusBar()
|
showStatusBar()
|
||||||
navigationController.setNavigationBarHidden(false, animated: true)
|
|
||||||
|
// We delay the showing of the navigation bars because it freaks out on iOS 15 with the new split view controller
|
||||||
|
// if it is trying to show at the same time as the show timeline animation
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
navigationController.setNavigationBarHidden(false, animated: true)
|
||||||
|
}
|
||||||
navigationController.setToolbarHidden(false, animated: true)
|
navigationController.setToolbarHidden(false, animated: true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private extension SceneCoordinator {
|
private extension SceneCoordinator {
|
||||||
|
|
||||||
|
func configureNavigationController(_ navController: UINavigationController) {
|
||||||
|
|
||||||
|
let scrollEdge = UINavigationBarAppearance()
|
||||||
|
scrollEdge.configureWithOpaqueBackground()
|
||||||
|
scrollEdge.shadowColor = nil
|
||||||
|
scrollEdge.shadowImage = UIImage()
|
||||||
|
|
||||||
|
let standard = UINavigationBarAppearance()
|
||||||
|
standard.shadowColor = .opaqueSeparator
|
||||||
|
standard.shadowImage = UIImage()
|
||||||
|
|
||||||
|
navController.navigationBar.standardAppearance = standard
|
||||||
|
navController.navigationBar.compactAppearance = standard
|
||||||
|
navController.navigationBar.scrollEdgeAppearance = scrollEdge
|
||||||
|
navController.navigationBar.compactScrollEdgeAppearance = scrollEdge
|
||||||
|
|
||||||
|
navController.navigationBar.tintColor = AppAssets.primaryAccentColor
|
||||||
|
|
||||||
|
let toolbarAppearance = UIToolbarAppearance()
|
||||||
|
navController.toolbar.standardAppearance = toolbarAppearance
|
||||||
|
navController.toolbar.compactAppearance = toolbarAppearance
|
||||||
|
navController.toolbar.scrollEdgeAppearance = toolbarAppearance
|
||||||
|
navController.toolbar.tintColor = AppAssets.primaryAccentColor
|
||||||
|
}
|
||||||
|
|
||||||
func markArticlesWithUndo(_ articles: [Article], statusKey: ArticleStatus.Key, flag: Bool, completion: (() -> Void)? = nil) {
|
func markArticlesWithUndo(_ articles: [Article], statusKey: ArticleStatus.Key, flag: Bool, completion: (() -> Void)? = nil) {
|
||||||
guard let undoManager = undoManager,
|
guard let undoManager = undoManager,
|
||||||
let markReadCommand = MarkStatusCommand(initialArticles: articles, statusKey: statusKey, flag: flag, undoManager: undoManager, completion: completion) else {
|
let markReadCommand = MarkStatusCommand(initialArticles: articles, statusKey: statusKey, flag: flag, undoManager: undoManager, completion: completion) else {
|
||||||
@ -1429,7 +1445,7 @@ private extension SceneCoordinator {
|
|||||||
func rebuildArticleDictionaries() {
|
func rebuildArticleDictionaries() {
|
||||||
var idDictionary = [String: Article]()
|
var idDictionary = [String: Article]()
|
||||||
|
|
||||||
articles.forEach { article in
|
for article in articles {
|
||||||
idDictionary[article.articleID] = article
|
idDictionary[article.articleID] = article
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1500,7 +1516,7 @@ private extension SceneCoordinator {
|
|||||||
|
|
||||||
updateExpandedNodes?()
|
updateExpandedNodes?()
|
||||||
let changes = rebuildShadowTable()
|
let changes = rebuildShadowTable()
|
||||||
feedsViewController.reloadFeeds(initialLoad: initialLoad, changes: changes, completion: completion)
|
sidebarViewController.reloadFeeds(initialLoad: initialLoad, changes: changes, completion: completion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1514,15 +1530,17 @@ private extension SceneCoordinator {
|
|||||||
|
|
||||||
if isExpanded(sectionNode) {
|
if isExpanded(sectionNode) {
|
||||||
for node in sectionNode.childNodes {
|
for node in sectionNode.childNodes {
|
||||||
feedNodes.append(FeedNode(node))
|
let feedNode = FeedNode(node)
|
||||||
|
feedNodes.append(feedNode)
|
||||||
if isExpanded(node) {
|
if isExpanded(node) {
|
||||||
for child in node.childNodes {
|
for child in node.childNodes {
|
||||||
feedNodes.append(FeedNode(child))
|
let childNode = FeedNode(child)
|
||||||
|
feedNodes.append(childNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let sectionID = (sectionNode.representedObject as? Account)?.accountID ?? ""
|
let sectionID = (sectionNode.representedObject as? Account)?.accountID ?? ""
|
||||||
newShadowTable.append((sectionID: sectionID, feedNodes: feedNodes))
|
newShadowTable.append((sectionID: sectionID, feedNodes: feedNodes))
|
||||||
}
|
}
|
||||||
@ -1532,9 +1550,10 @@ private extension SceneCoordinator {
|
|||||||
currentFeedIndexPath = indexPathFor(timelineFeed as AnyObject)
|
currentFeedIndexPath = indexPathFor(timelineFeed as AnyObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the differences in the shadow table rows
|
// Compute the differences in the shadow table rows and the expanded table entries
|
||||||
var changes = [ShadowTableChanges.RowChanges]()
|
var changes = [ShadowTableChanges.RowChanges]()
|
||||||
|
let expandedTableDifference = lastExpandedTable.symmetricDifference(expandedTable)
|
||||||
|
|
||||||
for (section, newSectionRows) in newShadowTable.enumerated() {
|
for (section, newSectionRows) in newShadowTable.enumerated() {
|
||||||
var moves = Set<ShadowTableChanges.Move>()
|
var moves = Set<ShadowTableChanges.Move>()
|
||||||
var inserts = Set<Int>()
|
var inserts = Set<Int>()
|
||||||
@ -1560,9 +1579,22 @@ private extension SceneCoordinator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changes.append(ShadowTableChanges.RowChanges(section: section, deletes: deletes, inserts: inserts, moves: moves))
|
// We need to reload the difference in expanded rows to get the disclosure arrows correct when programmatically changing their state
|
||||||
|
var reloads = Set<Int>()
|
||||||
|
|
||||||
|
for (index, newFeedNode) in newSectionRows.feedNodes.enumerated() {
|
||||||
|
if let newFeedNodeContainerID = (newFeedNode.node.representedObject as? Container)?.containerID {
|
||||||
|
if expandedTableDifference.contains(newFeedNodeContainerID) {
|
||||||
|
reloads.insert(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changes.append(ShadowTableChanges.RowChanges(section: section, deletes: deletes, inserts: inserts, reloads: reloads, moves: moves))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastExpandedTable = expandedTable
|
||||||
|
|
||||||
// Compute the difference in the shadow table sections
|
// Compute the difference in the shadow table sections
|
||||||
var moves = Set<ShadowTableChanges.Move>()
|
var moves = Set<ShadowTableChanges.Move>()
|
||||||
var inserts = Set<Int>()
|
var inserts = Set<Int>()
|
||||||
@ -1610,13 +1642,6 @@ private extension SceneCoordinator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func indexPathFor(_ object: AnyObject) -> IndexPath? {
|
|
||||||
guard let node = treeController.rootNode.descendantNodeRepresentingObject(object) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return indexPathFor(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setTimelineFeed(_ feed: SidebarItem?, animated: Bool, completion: (() -> Void)? = nil) {
|
func setTimelineFeed(_ feed: SidebarItem?, animated: Bool, completion: (() -> Void)? = nil) {
|
||||||
timelineFeed = feed
|
timelineFeed = feed
|
||||||
|
|
||||||
@ -1986,9 +2011,11 @@ private extension SceneCoordinator {
|
|||||||
func fetchAndReplaceArticlesAsync(animated: Bool, completion: @escaping () -> Void) {
|
func fetchAndReplaceArticlesAsync(animated: Bool, completion: @escaping () -> Void) {
|
||||||
// To be called when we need to do an entire fetch, but an async delay is okay.
|
// To be called when we need to do an entire fetch, but an async delay is okay.
|
||||||
// Example: we have the Today feed selected, and the calendar day just changed.
|
// Example: we have the Today feed selected, and the calendar day just changed.
|
||||||
|
|
||||||
cancelPendingAsyncFetches()
|
cancelPendingAsyncFetches()
|
||||||
|
emptyTheTimeline()
|
||||||
|
|
||||||
guard let timelineFeed = timelineFeed else {
|
guard let timelineFeed = timelineFeed else {
|
||||||
emptyTheTimeline()
|
|
||||||
completion()
|
completion()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2004,7 +2031,6 @@ private extension SceneCoordinator {
|
|||||||
self?.replaceArticles(with: articles, animated: animated)
|
self?.replaceArticles(with: articles, animated: animated)
|
||||||
completion()
|
completion()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchUnsortedArticlesAsync(for representedObjects: [Any], completion: @escaping ArticleSetBlock) {
|
func fetchUnsortedArticlesAsync(for representedObjects: [Any], completion: @escaping ArticleSetBlock) {
|
||||||
@ -2060,137 +2086,9 @@ private extension SceneCoordinator {
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Three Panel Mode
|
|
||||||
|
|
||||||
func installTimelineControllerIfNecessary(animated: Bool) {
|
|
||||||
if navControllerForTimeline().viewControllers.filter({ $0 is TimelineViewController }).count < 1 {
|
|
||||||
isTimelineViewControllerPending = true
|
|
||||||
timelineViewController = UIStoryboard.main.instantiateController(ofType: TimelineViewController.self)
|
|
||||||
timelineViewController!.coordinator = self
|
|
||||||
navControllerForTimeline().pushViewController(timelineViewController!, animated: animated)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
func installArticleController(state: ArticleViewController.State? = nil, animated: Bool) -> ArticleViewController {
|
|
||||||
|
|
||||||
isArticleViewControllerPending = true
|
|
||||||
|
|
||||||
let articleController = UIStoryboard.main.instantiateController(ofType: ArticleViewController.self)
|
|
||||||
articleController.coordinator = self
|
|
||||||
articleController.article = currentArticle
|
|
||||||
articleController.restoreState = state
|
|
||||||
|
|
||||||
if let subSplit = subSplitViewController {
|
|
||||||
let controller = addNavControllerIfNecessary(articleController, showButton: false)
|
|
||||||
subSplit.showDetailViewController(controller, sender: self)
|
|
||||||
} else if rootSplitViewController.isCollapsed || wasRootSplitViewControllerCollapsed {
|
|
||||||
navigationController.pushViewController(articleController, animated: animated)
|
|
||||||
} else {
|
|
||||||
let controller = addNavControllerIfNecessary(articleController, showButton: true)
|
|
||||||
rootSplitViewController.showDetailViewController(controller, sender: self)
|
|
||||||
}
|
|
||||||
|
|
||||||
return articleController
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func addNavControllerIfNecessary(_ controller: UIViewController, showButton: Bool) -> UIViewController {
|
|
||||||
|
|
||||||
// You will sometimes get a compact horizontal size class while in three panel mode. Dunno why it lies.
|
|
||||||
if rootSplitViewController.traitCollection.horizontalSizeClass == .compact && !isThreePanelMode {
|
|
||||||
|
|
||||||
return controller
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
let navController = InteractiveNavigationController.template(rootViewController: controller)
|
|
||||||
navController.isToolbarHidden = false
|
|
||||||
|
|
||||||
if showButton {
|
|
||||||
controller.navigationItem.leftBarButtonItem = rootSplitViewController.displayModeButtonItem
|
|
||||||
controller.navigationItem.leftItemsSupplementBackButton = true
|
|
||||||
} else {
|
|
||||||
controller.navigationItem.leftBarButtonItem = nil
|
|
||||||
controller.navigationItem.leftItemsSupplementBackButton = false
|
|
||||||
}
|
|
||||||
|
|
||||||
return navController
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func installSubSplit() {
|
|
||||||
rootSplitViewController.preferredPrimaryColumnWidthFraction = 0.30
|
|
||||||
|
|
||||||
subSplitViewController = UISplitViewController()
|
|
||||||
subSplitViewController!.preferredDisplayMode = .oneBesideSecondary
|
|
||||||
subSplitViewController!.viewControllers = [InteractiveNavigationController.template()]
|
|
||||||
subSplitViewController!.preferredPrimaryColumnWidthFraction = 0.4285
|
|
||||||
|
|
||||||
rootSplitViewController.showDetailViewController(subSplitViewController!, sender: self)
|
|
||||||
rootSplitViewController.setOverrideTraitCollection(UITraitCollection(horizontalSizeClass: .regular), forChild: subSplitViewController!)
|
|
||||||
}
|
|
||||||
|
|
||||||
func navControllerForTimeline() -> UINavigationController {
|
|
||||||
if let subSplit = subSplitViewController {
|
|
||||||
return subSplit.viewControllers.first as! UINavigationController
|
|
||||||
} else {
|
|
||||||
return navigationController
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func configureThreePanelMode() {
|
|
||||||
articleViewController?.stopArticleExtractorIfProcessing()
|
|
||||||
let articleViewControllerState = articleViewController?.currentState
|
|
||||||
defer {
|
|
||||||
navigationController.viewControllers = [feedsViewController]
|
|
||||||
}
|
|
||||||
|
|
||||||
if rootSplitViewController.viewControllers.last is InteractiveNavigationController {
|
|
||||||
_ = rootSplitViewController.viewControllers.popLast()
|
|
||||||
}
|
|
||||||
|
|
||||||
installSubSplit()
|
|
||||||
installTimelineControllerIfNecessary(animated: false)
|
|
||||||
timelineViewController?.navigationItem.leftBarButtonItem = rootSplitViewController.displayModeButtonItem
|
|
||||||
timelineViewController?.navigationItem.leftItemsSupplementBackButton = true
|
|
||||||
|
|
||||||
installArticleController(state: articleViewControllerState, animated: false)
|
|
||||||
|
|
||||||
feedsViewController.restoreSelectionIfNecessary(adjustScroll: true)
|
|
||||||
timelineViewController!.restoreSelectionIfNecessary(adjustScroll: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func configureStandardPanelMode() {
|
|
||||||
articleViewController?.stopArticleExtractorIfProcessing()
|
|
||||||
let articleViewControllerState = articleViewController?.currentState
|
|
||||||
rootSplitViewController.preferredPrimaryColumnWidthFraction = UISplitViewController.automaticDimension
|
|
||||||
|
|
||||||
// Set the is Pending flags early to prevent the navigation controller delegate from thinking that we
|
|
||||||
// swiping around in the user interface
|
|
||||||
isTimelineViewControllerPending = true
|
|
||||||
isArticleViewControllerPending = true
|
|
||||||
|
|
||||||
navigationController.viewControllers = [feedsViewController]
|
|
||||||
if rootSplitViewController.viewControllers.last is UISplitViewController {
|
|
||||||
subSplitViewController = nil
|
|
||||||
_ = rootSplitViewController.viewControllers.popLast()
|
|
||||||
}
|
|
||||||
|
|
||||||
if currentFeedIndexPath != nil {
|
|
||||||
timelineViewController = UIStoryboard.main.instantiateController(ofType: TimelineViewController.self)
|
|
||||||
timelineViewController!.coordinator = self
|
|
||||||
navigationController.pushViewController(timelineViewController!, animated: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
installArticleController(state: articleViewControllerState, animated: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: NSUserActivity
|
// MARK: NSUserActivity
|
||||||
|
|
||||||
func windowState() -> [AnyHashable: Any] {
|
func windowState() -> [AnyHashable: Any] {
|
||||||
let containerExpandedWindowState = expandedTable.map( { $0.userInfo })
|
let containerExpandedWindowState = expandedTable.map( { $0.userInfo })
|
||||||
var readArticlesFilterState = [[AnyHashable: AnyHashable]: Bool]()
|
var readArticlesFilterState = [[AnyHashable: AnyHashable]: Bool]()
|
||||||
@ -2200,7 +2098,8 @@ private extension SceneCoordinator {
|
|||||||
return [
|
return [
|
||||||
UserInfoKey.readFeedsFilterState: isReadFeedsFiltered,
|
UserInfoKey.readFeedsFilterState: isReadFeedsFiltered,
|
||||||
UserInfoKey.containerExpandedWindowState: containerExpandedWindowState,
|
UserInfoKey.containerExpandedWindowState: containerExpandedWindowState,
|
||||||
UserInfoKey.readArticlesFilterState: readArticlesFilterState
|
UserInfoKey.readArticlesFilterState: readArticlesFilterState,
|
||||||
|
UserInfoKey.isSidebarHidden: rootSplitViewController.displayMode == .secondaryOnly
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2223,7 +2122,7 @@ private extension SceneCoordinator {
|
|||||||
self.treeControllerDelegate.resetFilterExceptions()
|
self.treeControllerDelegate.resetFilterExceptions()
|
||||||
if let indexPath = self.indexPathFor(smartFeed) {
|
if let indexPath = self.indexPathFor(smartFeed) {
|
||||||
self.selectFeed(indexPath: indexPath) {
|
self.selectFeed(indexPath: indexPath) {
|
||||||
self.feedsViewController.focus()
|
self.sidebarViewController.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -2244,7 +2143,7 @@ private extension SceneCoordinator {
|
|||||||
|
|
||||||
if let folderNode = self.findFolderNode(folderName: folderName, beginningAt: accountNode), let indexPath = self.indexPathFor(folderNode) {
|
if let folderNode = self.findFolderNode(folderName: folderName, beginningAt: accountNode), let indexPath = self.indexPathFor(folderNode) {
|
||||||
self.selectFeed(indexPath: indexPath) {
|
self.selectFeed(indexPath: indexPath) {
|
||||||
self.feedsViewController.focus()
|
self.sidebarViewController.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -2256,8 +2155,8 @@ private extension SceneCoordinator {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.discloseFeed(feed) {
|
self.discloseFeed(feed, initialLoad: true) {
|
||||||
self.feedsViewController.focus()
|
self.sidebarViewController.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2281,11 +2180,11 @@ private extension SceneCoordinator {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let Feed = account.existingFeed(withFeedID: feedID) else {
|
guard let feed = account.existingFeed(withFeedID: feedID) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
discloseFeed(Feed) {
|
discloseFeed(feed) {
|
||||||
self.selectArticleInCurrentFeed(articleID)
|
self.selectArticleInCurrentFeed(articleID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,37 +11,38 @@ import UserNotifications
|
|||||||
import Account
|
import Account
|
||||||
|
|
||||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
|
|
||||||
var window: UIWindow?
|
var window: UIWindow?
|
||||||
var coordinator = SceneCoordinator()
|
var coordinator: SceneCoordinator!
|
||||||
|
|
||||||
// UIWindowScene delegate
|
// UIWindowScene delegate
|
||||||
|
|
||||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||||
|
|
||||||
window = UIWindow(windowScene: scene as! UIWindowScene)
|
|
||||||
window!.tintColor = AppAssets.primaryAccentColor
|
window!.tintColor = AppAssets.primaryAccentColor
|
||||||
updateUserInterfaceStyle()
|
updateUserInterfaceStyle()
|
||||||
window!.rootViewController = coordinator.start(for: window!.frame.size)
|
|
||||||
|
let rootViewController = window!.rootViewController as! RootSplitViewController
|
||||||
|
coordinator = SceneCoordinator(rootSplitViewController: rootViewController)
|
||||||
|
rootViewController.coordinator = coordinator
|
||||||
|
rootViewController.delegate = coordinator
|
||||||
|
rootViewController.showsSecondaryOnlyButton = true
|
||||||
|
|
||||||
coordinator.restoreWindowState(session.stateRestorationActivity)
|
coordinator.restoreWindowState(session.stateRestorationActivity)
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange), name: UserDefaults.didChangeNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange), name: UserDefaults.didChangeNotification, object: nil)
|
||||||
|
|
||||||
if let _ = connectionOptions.urlContexts.first?.url {
|
if let _ = connectionOptions.urlContexts.first?.url {
|
||||||
window?.makeKeyAndVisible()
|
|
||||||
self.scene(scene, openURLContexts: connectionOptions.urlContexts)
|
self.scene(scene, openURLContexts: connectionOptions.urlContexts)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let shortcutItem = connectionOptions.shortcutItem {
|
if let shortcutItem = connectionOptions.shortcutItem {
|
||||||
window!.makeKeyAndVisible()
|
|
||||||
handleShortcutItem(shortcutItem)
|
handleShortcutItem(shortcutItem)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let notificationResponse = connectionOptions.notificationResponse {
|
if let notificationResponse = connectionOptions.notificationResponse {
|
||||||
window!.makeKeyAndVisible()
|
|
||||||
coordinator.handle(notificationResponse)
|
coordinator.handle(notificationResponse)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -49,8 +50,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||||||
if let userActivity = connectionOptions.userActivities.first ?? session.stateRestorationActivity {
|
if let userActivity = connectionOptions.userActivities.first ?? session.stateRestorationActivity {
|
||||||
coordinator.handle(userActivity)
|
coordinator.handle(userActivity)
|
||||||
}
|
}
|
||||||
|
|
||||||
window!.makeKeyAndVisible()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
|
func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
|
||||||
@ -73,7 +72,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||||||
func sceneWillEnterForeground(_ scene: UIScene) {
|
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||||
appDelegate.resumeDatabaseProcessingIfNecessary()
|
appDelegate.resumeDatabaseProcessingIfNecessary()
|
||||||
appDelegate.prepareAccountsForForeground()
|
appDelegate.prepareAccountsForForeground()
|
||||||
coordinator.configurePanelMode(for: window!.frame.size)
|
|
||||||
coordinator.resetFocus()
|
coordinator.resetFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +94,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||||||
coordinator.cleanUp(conditional: conditional)
|
coordinator.cleanUp(conditional: conditional)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func presentError(_ error: Error) {
|
||||||
|
self.window!.rootViewController?.presentError(error)
|
||||||
|
}
|
||||||
|
|
||||||
// Handle Opening of URLs
|
// Handle Opening of URLs
|
||||||
|
|
||||||
func scene(_ scene: UIScene, openURLContexts urlContexts: Set<UIOpenURLContext>) {
|
func scene(_ scene: UIScene, openURLContexts urlContexts: Set<UIOpenURLContext>) {
|
||||||
|
@ -116,7 +116,6 @@ class TimelineViewController: UITableViewController, UndoableCommandRunner {
|
|||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
super.viewDidAppear(true)
|
super.viewDidAppear(true)
|
||||||
coordinator.isTimelineViewControllerPending = false
|
|
||||||
|
|
||||||
if navigationController?.navigationBar.alpha == 0 {
|
if navigationController?.navigationBar.alpha == 0 {
|
||||||
UIView.animate(withDuration: 0.5) {
|
UIView.animate(withDuration: 0.5) {
|
||||||
@ -605,10 +604,6 @@ private extension TimelineViewController {
|
|||||||
|
|
||||||
func configureToolbar() {
|
func configureToolbar() {
|
||||||
|
|
||||||
guard !coordinator.isThreePanelMode else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let refreshProgressView = Bundle.main.loadNibNamed("RefreshProgressView", owner: self, options: nil)?[0] as? RefreshProgressView else {
|
guard let refreshProgressView = Bundle.main.loadNibNamed("RefreshProgressView", owner: self, options: nil)?[0] as? RefreshProgressView else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user