mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Merge branch 'ios-ui-notifications' of https://github.com/stuartbreckenridge/NetNewsWire into ios-ui-notifications
This commit is contained in:
@@ -257,6 +257,10 @@ struct AppAssets {
|
||||
let image = UIImage(systemName: "star.fill")!
|
||||
return IconImage(image, isSymbol: true, isBackgroundSupressed: true, preferredColor: AppAssets.starColor.cgColor)
|
||||
}
|
||||
|
||||
static var themeImage: UIImage = {
|
||||
return UIImage(systemName: "doc.richtext")!
|
||||
}()
|
||||
|
||||
static var tickMarkColor: UIColor = {
|
||||
return UIColor(named: "tickMarkColor")!
|
||||
|
||||
@@ -28,7 +28,7 @@ enum UserInterfaceColorPalette: Int, CustomStringConvertible, CaseIterable {
|
||||
|
||||
final class AppDefaults {
|
||||
|
||||
static let defaultThemeName = "Defaults"
|
||||
static let defaultThemeName = "Default"
|
||||
|
||||
static let shared = AppDefaults()
|
||||
private init() {}
|
||||
@@ -168,15 +168,6 @@ final class AppDefaults {
|
||||
}
|
||||
}
|
||||
|
||||
var articleFullscreenAvailable: Bool {
|
||||
get {
|
||||
return AppDefaults.bool(for: Key.articleFullscreenAvailable)
|
||||
}
|
||||
set {
|
||||
AppDefaults.setBool(for: Key.articleFullscreenAvailable, newValue)
|
||||
}
|
||||
}
|
||||
|
||||
var articleFullscreenEnabled: Bool {
|
||||
get {
|
||||
return AppDefaults.bool(for: Key.articleFullscreenEnabled)
|
||||
|
||||
@@ -12,19 +12,20 @@ import Account
|
||||
import Articles
|
||||
import SafariServices
|
||||
|
||||
class ArticleViewController: UIViewController {
|
||||
class ArticleViewController: UIViewController, MainControllerIdentifiable {
|
||||
|
||||
typealias State = (extractedArticle: ExtractedArticle?,
|
||||
isShowingExtractedArticle: Bool,
|
||||
articleExtractorButtonState: ArticleExtractorButtonState,
|
||||
windowScrollY: Int)
|
||||
|
||||
isShowingExtractedArticle: Bool,
|
||||
articleExtractorButtonState: ArticleExtractorButtonState,
|
||||
windowScrollY: Int)
|
||||
|
||||
@IBOutlet private weak var nextUnreadBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet private weak var prevArticleBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet private weak var nextArticleBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet private weak var readBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet private weak var starBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet private weak var actionBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet private weak var appearanceBarButtonItem: UIBarButtonItem!
|
||||
|
||||
@IBOutlet private var searchBar: ArticleSearchBar!
|
||||
@IBOutlet private var searchBarBottomConstraint: NSLayoutConstraint!
|
||||
@@ -43,8 +44,12 @@ class ArticleViewController: UIViewController {
|
||||
return button
|
||||
}()
|
||||
|
||||
var mainControllerIdentifer = MainControllerIdentifier.article
|
||||
|
||||
weak var coordinator: SceneCoordinator!
|
||||
|
||||
private let poppableDelegate = PoppableGestureRecognizerDelegate()
|
||||
|
||||
var article: Article? {
|
||||
didSet {
|
||||
if let controller = currentWebViewController, controller.article != article {
|
||||
@@ -84,34 +89,33 @@ class ArticleViewController: UIViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
|
||||
|
||||
let fullScreenTapZone = UIView()
|
||||
NSLayoutConstraint.activate([
|
||||
fullScreenTapZone.widthAnchor.constraint(equalToConstant: 150),
|
||||
fullScreenTapZone.heightAnchor.constraint(equalToConstant: 44)
|
||||
])
|
||||
fullScreenTapZone.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapNavigationBar)))
|
||||
navigationItem.titleView = fullScreenTapZone
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(reloadDueToThemeChange(_:)), name: .CurrentArticleThemeDidChangeNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(configureAppearanceMenu(_:)), name: .ArticleThemeNamesDidChangeNotification, object: nil)
|
||||
|
||||
articleExtractorButton.addTarget(self, action: #selector(toggleArticleExtractor(_:)), for: .touchUpInside)
|
||||
toolbarItems?.insert(UIBarButtonItem(customView: articleExtractorButton), at: 6)
|
||||
|
||||
if let parentNavController = navigationController?.parent as? UINavigationController {
|
||||
poppableDelegate.navigationController = parentNavController
|
||||
parentNavController.interactivePopGestureRecognizer?.delegate = poppableDelegate
|
||||
}
|
||||
|
||||
pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:])
|
||||
pageViewController.delegate = self
|
||||
pageViewController.dataSource = self
|
||||
|
||||
|
||||
// This code is to disallow paging if we scroll from the left edge. If this code is removed
|
||||
// PoppableGestureRecognizerDelegate will allow us to both navigate back and page back at the
|
||||
// same time. That is really weird when it happens.
|
||||
let panGestureRecognizer = UIPanGestureRecognizer()
|
||||
panGestureRecognizer.delegate = self
|
||||
pageViewController.scrollViewInsidePageControl?.addGestureRecognizer(panGestureRecognizer)
|
||||
|
||||
|
||||
pageViewController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(pageViewController.view)
|
||||
addChild(pageViewController!)
|
||||
@@ -121,7 +125,7 @@ class ArticleViewController: UIViewController {
|
||||
view.topAnchor.constraint(equalTo: pageViewController.view.topAnchor),
|
||||
view.bottomAnchor.constraint(equalTo: pageViewController.view.bottomAnchor)
|
||||
])
|
||||
|
||||
|
||||
let controller: WebViewController
|
||||
if let state = restoreState {
|
||||
controller = createWebViewController(article, updateView: false)
|
||||
@@ -136,13 +140,10 @@ class ArticleViewController: UIViewController {
|
||||
if let rsp = restoreScrollPosition {
|
||||
controller.setScrollPosition(isShowingExtractedArticle: rsp.isShowingExtractedArticle, articleWindowScrollY: rsp.articleWindowScrollY)
|
||||
}
|
||||
|
||||
|
||||
articleExtractorButton.buttonState = controller.articleExtractorButtonState
|
||||
|
||||
self.pageViewController.setViewControllers([controller], direction: .forward, animated: false, completion: nil)
|
||||
if AppDefaults.shared.articleFullscreenEnabled {
|
||||
controller.hideBars()
|
||||
}
|
||||
|
||||
// Search bar
|
||||
searchBar.translatesAutoresizingMaskIntoConstraints = false
|
||||
@@ -152,15 +153,21 @@ class ArticleViewController: UIViewController {
|
||||
searchBar.delegate = self
|
||||
view.bringSubviewToFront(searchBar)
|
||||
|
||||
configureAppearanceMenu()
|
||||
updateUI()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(true)
|
||||
coordinator.isArticleViewControllerPending = false
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
navigationController?.isToolbarHidden = false
|
||||
if AppDefaults.shared.articleFullscreenEnabled {
|
||||
currentWebViewController?.hideBars()
|
||||
}
|
||||
|
||||
super.viewWillAppear(animated)
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
if searchBar != nil && !searchBar.isHidden {
|
||||
endFind()
|
||||
}
|
||||
@@ -181,14 +188,16 @@ class ArticleViewController: UIViewController {
|
||||
readBarButtonItem.isEnabled = false
|
||||
starBarButtonItem.isEnabled = false
|
||||
actionBarButtonItem.isEnabled = false
|
||||
appearanceBarButtonItem.isEnabled = false
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
nextUnreadBarButtonItem.isEnabled = coordinator.isAnyUnreadAvailable
|
||||
prevArticleBarButtonItem.isEnabled = coordinator.isPrevArticleAvailable
|
||||
nextArticleBarButtonItem.isEnabled = coordinator.isNextArticleAvailable
|
||||
readBarButtonItem.isEnabled = true
|
||||
starBarButtonItem.isEnabled = true
|
||||
appearanceBarButtonItem.isEnabled = true
|
||||
|
||||
let permalinkPresent = article.preferredLink != nil
|
||||
var isFeedProvider = false
|
||||
@@ -218,6 +227,66 @@ class ArticleViewController: UIViewController {
|
||||
|
||||
}
|
||||
|
||||
override func contentScrollView(for edge: NSDirectionalRectEdge) -> UIScrollView? {
|
||||
return currentWebViewController?.webView?.scrollView
|
||||
}
|
||||
|
||||
@objc
|
||||
func configureAppearanceMenu(_ sender: Any? = nil) {
|
||||
|
||||
var themeActions = [UIAction]()
|
||||
|
||||
for themeName in ArticleThemesManager.shared.themeNames {
|
||||
let action = UIAction(title: themeName,
|
||||
image: nil,
|
||||
identifier: nil,
|
||||
discoverabilityTitle: nil,
|
||||
attributes: [],
|
||||
state: ArticleThemesManager.shared.currentThemeName == themeName ? .on : .off,
|
||||
handler: { action in
|
||||
ArticleThemesManager.shared.currentThemeName = themeName
|
||||
})
|
||||
themeActions.append(action)
|
||||
}
|
||||
|
||||
let defaultThemeAction = UIAction(title: NSLocalizedString("Default", comment: "Default"), image: nil, identifier: nil, discoverabilityTitle: nil, attributes: [], state: ArticleThemesManager.shared.currentThemeName == AppDefaults.defaultThemeName ? .on : .off) { _ in
|
||||
ArticleThemesManager.shared.currentThemeName = AppDefaults.defaultThemeName
|
||||
}
|
||||
themeActions.append(defaultThemeAction)
|
||||
|
||||
let themeMenu = UIMenu(title: "Theme", image: AppAssets.themeImage, identifier: nil, options: .singleSelection, children: themeActions)
|
||||
themeMenu.subtitle = NSLocalizedString("Change the look of articles.", comment: "Change theme")
|
||||
|
||||
var children: [UIMenuElement] = [themeMenu]
|
||||
|
||||
if let currentWebViewController = currentWebViewController {
|
||||
if currentWebViewController.isFullScreenAvailable {
|
||||
let fullScreenAction = UIAction(title: NSLocalizedString("Full Screen", comment: "Full Screen"),
|
||||
image: UIImage(systemName: "arrow.up.backward.and.arrow.down.forward"),
|
||||
identifier: nil,
|
||||
discoverabilityTitle: nil,
|
||||
attributes: [],
|
||||
state: .off) { [weak self] _ in
|
||||
self?.currentWebViewController?.hideBars()
|
||||
}
|
||||
fullScreenAction.subtitle = NSLocalizedString("Tap the top of the screen to exit Full Screen.", comment: "Exit criteria.")
|
||||
children.append(fullScreenAction)
|
||||
}
|
||||
}
|
||||
|
||||
let appearanceMenu = UIMenu(title: NSLocalizedString("Article Appearance", comment: "Appearance"), image: UIImage(systemName: "textformat.size") , identifier: nil, options: .displayInline, children: children)
|
||||
|
||||
appearanceBarButtonItem.menu = appearanceMenu
|
||||
|
||||
}
|
||||
|
||||
@objc
|
||||
func reloadDueToThemeChange(_ notification: Notification) {
|
||||
currentWebViewController?.fullReload()
|
||||
configureAppearanceMenu()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
@objc dynamic func unreadCountDidChange(_ notification: Notification) {
|
||||
@@ -235,7 +304,7 @@ class ArticleViewController: UIViewController {
|
||||
updateUI()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@objc func contentSizeCategoryDidChange(_ note: Notification) {
|
||||
currentWebViewController?.fullReload()
|
||||
}
|
||||
@@ -248,15 +317,11 @@ class ArticleViewController: UIViewController {
|
||||
}
|
||||
|
||||
// MARK: Actions
|
||||
|
||||
@objc func didTapNavigationBar() {
|
||||
currentWebViewController?.hideBars()
|
||||
}
|
||||
|
||||
|
||||
@objc func showBars(_ sender: Any) {
|
||||
currentWebViewController?.showBars()
|
||||
}
|
||||
|
||||
|
||||
@IBAction func toggleArticleExtractor(_ sender: Any) {
|
||||
currentWebViewController?.toggleArticleExtractor()
|
||||
}
|
||||
@@ -284,35 +349,35 @@ class ArticleViewController: UIViewController {
|
||||
@IBAction func showActivityDialog(_ sender: Any) {
|
||||
currentWebViewController?.showActivityDialog(popOverBarButtonItem: actionBarButtonItem)
|
||||
}
|
||||
|
||||
|
||||
@objc func toggleReaderView(_ sender: Any?) {
|
||||
currentWebViewController?.toggleArticleExtractor()
|
||||
}
|
||||
|
||||
// MARK: Keyboard Shortcuts
|
||||
|
||||
|
||||
@objc func navigateToTimeline(_ sender: Any?) {
|
||||
coordinator.navigateToTimeline()
|
||||
}
|
||||
|
||||
// MARK: API
|
||||
|
||||
|
||||
func focus() {
|
||||
currentWebViewController?.focus()
|
||||
}
|
||||
|
||||
|
||||
func canScrollDown() -> Bool {
|
||||
return currentWebViewController?.canScrollDown() ?? false
|
||||
}
|
||||
|
||||
|
||||
func canScrollUp() -> Bool {
|
||||
return currentWebViewController?.canScrollUp() ?? false
|
||||
}
|
||||
|
||||
|
||||
func scrollPageDown() {
|
||||
currentWebViewController?.scrollPageDown()
|
||||
}
|
||||
|
||||
|
||||
func scrollPageUp() {
|
||||
currentWebViewController?.scrollPageUp()
|
||||
}
|
||||
@@ -320,7 +385,7 @@ class ArticleViewController: UIViewController {
|
||||
func stopArticleExtractorIfProcessing() {
|
||||
currentWebViewController?.stopArticleExtractorIfProcessing()
|
||||
}
|
||||
|
||||
|
||||
func openInAppBrowser() {
|
||||
currentWebViewController?.openInAppBrowser()
|
||||
}
|
||||
@@ -387,9 +452,9 @@ extension ArticleViewController {
|
||||
|
||||
@objc func keyboardWillChangeFrame(_ notification: Notification) {
|
||||
if !searchBar.isHidden,
|
||||
let duration = notification.userInfo?[UIWindow.keyboardAnimationDurationUserInfoKey] as? Double,
|
||||
let curveRaw = notification.userInfo?[UIWindow.keyboardAnimationCurveUserInfoKey] as? UInt,
|
||||
let frame = notification.userInfo?[UIWindow.keyboardFrameEndUserInfoKey] as? CGRect {
|
||||
let duration = notification.userInfo?[UIWindow.keyboardAnimationDurationUserInfoKey] as? Double,
|
||||
let curveRaw = notification.userInfo?[UIWindow.keyboardAnimationCurveUserInfoKey] as? UInt,
|
||||
let frame = notification.userInfo?[UIWindow.keyboardFrameEndUserInfoKey] as? CGRect {
|
||||
|
||||
let curve = UIView.AnimationOptions(rawValue: curveRaw)
|
||||
let newHeight = view.safeAreaLayoutGuide.layoutFrame.maxY - frame.minY
|
||||
@@ -422,19 +487,19 @@ extension ArticleViewController: UIPageViewControllerDataSource {
|
||||
|
||||
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
|
||||
guard let webViewController = viewController as? WebViewController,
|
||||
let currentArticle = webViewController.article,
|
||||
let article = coordinator.findPrevArticle(currentArticle) else {
|
||||
return nil
|
||||
}
|
||||
let currentArticle = webViewController.article,
|
||||
let article = coordinator.findPrevArticle(currentArticle) else {
|
||||
return nil
|
||||
}
|
||||
return createWebViewController(article)
|
||||
}
|
||||
|
||||
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
|
||||
guard let webViewController = viewController as? WebViewController,
|
||||
let currentArticle = webViewController.article,
|
||||
let article = coordinator.findNextArticle(currentArticle) else {
|
||||
return nil
|
||||
}
|
||||
let currentArticle = webViewController.article,
|
||||
let article = coordinator.findNextArticle(currentArticle) else {
|
||||
return nil
|
||||
}
|
||||
return createWebViewController(article)
|
||||
}
|
||||
|
||||
@@ -443,7 +508,7 @@ extension ArticleViewController: UIPageViewControllerDataSource {
|
||||
// MARK: UIPageViewControllerDelegate
|
||||
|
||||
extension ArticleViewController: UIPageViewControllerDelegate {
|
||||
|
||||
|
||||
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
|
||||
guard finished, completed else { return }
|
||||
guard let article = currentWebViewController?.article else { return }
|
||||
@@ -460,17 +525,17 @@ extension ArticleViewController: UIPageViewControllerDelegate {
|
||||
|
||||
extension ArticleViewController: UIGestureRecognizerDelegate {
|
||||
|
||||
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
let point = gestureRecognizer.location(in: nil)
|
||||
if point.x > 40 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ class PreloadedWebView: WKWebView {
|
||||
preferences.javaScriptCanOpenWindowsAutomatically = false
|
||||
|
||||
/// The defaults for `preferredContentMode` and `allowsContentJavaScript` are suitable
|
||||
/// and don't need to be explicity set.
|
||||
/// `allowsContentJavaScript` replaces `WKPreferences.javascriptEnbaled`.
|
||||
/// and don't need to be explicitly set.
|
||||
/// `allowsContentJavaScript` replaces `WKPreferences.javascriptEnabled`.
|
||||
let webpagePreferences = WKWebpagePreferences()
|
||||
|
||||
let configuration = WKWebViewConfiguration()
|
||||
|
||||
@@ -31,13 +31,13 @@ class WebViewController: UIViewController {
|
||||
private var topShowBarsViewConstraint: NSLayoutConstraint!
|
||||
private var bottomShowBarsViewConstraint: NSLayoutConstraint!
|
||||
|
||||
private var webView: PreloadedWebView? {
|
||||
var webView: PreloadedWebView? {
|
||||
return view.subviews[0] as? PreloadedWebView
|
||||
}
|
||||
|
||||
private lazy var contextMenuInteraction = UIContextMenuInteraction(delegate: self)
|
||||
private var isFullScreenAvailable: Bool {
|
||||
return AppDefaults.shared.articleFullscreenAvailable && traitCollection.userInterfaceIdiom == .phone && coordinator.isRootSplitCollapsed
|
||||
public var isFullScreenAvailable: Bool {
|
||||
return traitCollection.userInterfaceIdiom == .phone && coordinator.isRootSplitCollapsed
|
||||
}
|
||||
private lazy var transition = ImageTransition(controller: self)
|
||||
private var clickedImageCompletion: (() -> Void)?
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19529" 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"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
|
||||
@@ -17,7 +17,7 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="h1Q-FS-jlg" customClass="ArticleSearchBar" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<view hidden="YES" contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="h1Q-FS-jlg" customClass="ArticleSearchBar" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="782" width="414" height="31"/>
|
||||
<color key="backgroundColor" name="barBackgroundColor"/>
|
||||
</view>
|
||||
@@ -90,12 +90,18 @@
|
||||
<action selector="prevArticle:" destination="JEX-9P-axG" id="cMZ-tk-I4W"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem image="textformat.size" catalog="system" id="SoN-ax-tEE">
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="accLabelText" value="Appearance"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</barButtonItem>
|
||||
</rightBarButtonItems>
|
||||
</navigationItem>
|
||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
|
||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||
<connections>
|
||||
<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="nextUnreadBarButtonItem" destination="2w5-e9-C2V" id="Ekf-My-AHN"/>
|
||||
<outlet property="prevArticleBarButtonItem" destination="v4j-fq-23N" id="Gny-Oh-cQa"/>
|
||||
@@ -107,7 +113,7 @@
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="FJe-Yq-33r" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2318.840579710145" y="-759.375"/>
|
||||
<point key="canvasLocation" x="451" y="-431"/>
|
||||
</scene>
|
||||
<!--Timeline-->
|
||||
<scene sceneID="fag-XH-avP">
|
||||
@@ -153,12 +159,26 @@
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="nzm-Gf-Xce" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</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>
|
||||
<!--Feeds-->
|
||||
<scene sceneID="smW-Zh-WAh">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="MasterFeedViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" clearsSelectionOnViewWillAppear="NO" id="7bK-jq-Zjz" customClass="MasterFeedViewController" 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">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
@@ -216,7 +236,7 @@
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Rux-fX-hf1" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="900" y="-759"/>
|
||||
<point key="canvasLocation" x="452" y="-1794"/>
|
||||
</scene>
|
||||
<!--Image View Controller-->
|
||||
<scene sceneID="TT4-oA-DBw">
|
||||
@@ -407,6 +427,7 @@
|
||||
<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="star" catalog="system" width="128" height="116"/>
|
||||
<image name="textformat.size" catalog="system" width="128" height="80"/>
|
||||
<namedColor name="barBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</namedColor>
|
||||
|
||||
@@ -47,7 +47,11 @@ class KeyboardManager {
|
||||
|
||||
static func createKeyCommand(title: String, action: String, input: String, modifiers: UIKeyModifierFlags) -> UIKeyCommand {
|
||||
let selector = NSSelectorFromString(action)
|
||||
return UIKeyCommand(title: title, image: nil, action: selector, input: input, modifierFlags: modifiers, propertyList: nil, alternates: [], discoverabilityTitle: nil, attributes: [], state: .on)
|
||||
let keyCommand = UIKeyCommand(title: title, image: nil, action: selector, input: input, modifierFlags: modifiers, propertyList: nil, alternates: [], discoverabilityTitle: nil, attributes: [], state: .on)
|
||||
if #available(iOS 15.0, *) {
|
||||
keyCommand.wantsPriorityOverSystemBehavior = true
|
||||
}
|
||||
return keyCommand
|
||||
}
|
||||
|
||||
}
|
||||
@@ -62,7 +66,11 @@ private extension KeyboardManager {
|
||||
if let title = keyEntry["title"] as? String {
|
||||
return KeyboardManager.createKeyCommand(title: title, action: action, input: input, modifiers: modifiers)
|
||||
} else {
|
||||
return UIKeyCommand(input: input, modifierFlags: modifiers, action: NSSelectorFromString(action))
|
||||
let keyCommand = UIKeyCommand(input: input, modifierFlags: modifiers, action: NSSelectorFromString(action))
|
||||
if #available(iOS 15.0, *) {
|
||||
keyCommand.wantsPriorityOverSystemBehavior = true
|
||||
}
|
||||
return keyCommand
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ struct MasterFeedTableViewCellLayout {
|
||||
var rDisclosure = CGRect.zero
|
||||
if shouldShowDisclosure {
|
||||
rDisclosure.size = MasterFeedTableViewCellLayout.disclosureButtonSize
|
||||
rDisclosure.origin.x = bounds.origin.x
|
||||
rDisclosure.origin.x = insets.left
|
||||
}
|
||||
|
||||
// Favicon
|
||||
@@ -73,7 +73,7 @@ struct MasterFeedTableViewCellLayout {
|
||||
}
|
||||
|
||||
// Title
|
||||
var rLabelx = MasterFeedTableViewCellLayout.disclosureButtonSize.width
|
||||
var rLabelx = MasterFeedTableViewCellLayout.disclosureButtonSize.width + insets.left
|
||||
if itemIsInFolder {
|
||||
rLabelx += MasterFeedTableViewCellLayout.disclosureButtonSize.width - (rFavicon.width / 2)
|
||||
}
|
||||
|
||||
@@ -62,8 +62,6 @@ class MasterFeedTableViewSectionHeader: UITableViewHeaderFooterView {
|
||||
return label
|
||||
}()
|
||||
|
||||
private let unreadCountView = MasterFeedUnreadCountView(frame: CGRect.zero)
|
||||
|
||||
private lazy var disclosureButton: UIButton = {
|
||||
let button = NonIntrinsicButton()
|
||||
button.tintColor = AppAssets.secondaryAccentColor
|
||||
@@ -97,7 +95,7 @@ class MasterFeedTableViewSectionHeader: UITableViewHeaderFooterView {
|
||||
}
|
||||
|
||||
override func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||
let layout = MasterFeedTableViewSectionHeaderLayout(cellWidth: size.width, insets: safeAreaInsets, label: titleView, unreadCountView: unreadCountView)
|
||||
let layout = MasterFeedTableViewSectionHeaderLayout(cellWidth: size.width, insets: safeAreaInsets, label: titleView)
|
||||
return CGSize(width: bounds.width, height: layout.height)
|
||||
|
||||
}
|
||||
@@ -106,8 +104,7 @@ class MasterFeedTableViewSectionHeader: UITableViewHeaderFooterView {
|
||||
super.layoutSubviews()
|
||||
let layout = MasterFeedTableViewSectionHeaderLayout(cellWidth: contentView.bounds.size.width,
|
||||
insets: contentView.safeAreaInsets,
|
||||
label: titleView,
|
||||
unreadCountView: unreadCountView)
|
||||
label: titleView)
|
||||
layoutWith(layout)
|
||||
}
|
||||
|
||||
@@ -157,7 +154,6 @@ private extension MasterFeedTableViewSectionHeader {
|
||||
|
||||
func layoutWith(_ layout: MasterFeedTableViewSectionHeaderLayout) {
|
||||
titleView.setFrameIfNotEqual(layout.titleRect)
|
||||
unreadCountView.setFrameIfNotEqual(layout.unreadCountRect)
|
||||
disclosureButton.setFrameIfNotEqual(layout.disclosureButtonRect)
|
||||
|
||||
let top = CGRect(x: safeAreaInsets.left, y: 0, width: frame.width - safeAreaInsets.right - safeAreaInsets.left, height: 0.33)
|
||||
|
||||
@@ -12,19 +12,17 @@ import RSCore
|
||||
struct MasterFeedTableViewSectionHeaderLayout {
|
||||
|
||||
private static let labelMarginRight = CGFloat(integerLiteral: 8)
|
||||
private static let unreadCountMarginRight = CGFloat(integerLiteral: 0)
|
||||
private static let disclosureButtonSize = CGSize(width: 44, height: 44)
|
||||
private static let verticalPadding = CGFloat(integerLiteral: 11)
|
||||
|
||||
private static let minRowHeight = CGFloat(integerLiteral: 44)
|
||||
|
||||
let titleRect: CGRect
|
||||
let unreadCountRect: CGRect
|
||||
let disclosureButtonRect: CGRect
|
||||
|
||||
let height: CGFloat
|
||||
|
||||
init(cellWidth: CGFloat, insets: UIEdgeInsets, label: UILabel, unreadCountView: MasterFeedUnreadCountView) {
|
||||
init(cellWidth: CGFloat, insets: UIEdgeInsets, label: UILabel) {
|
||||
|
||||
let bounds = CGRect(x: insets.left, y: 0.0, width: floor(cellWidth - insets.right), height: 0.0)
|
||||
|
||||
@@ -33,35 +31,20 @@ struct MasterFeedTableViewSectionHeaderLayout {
|
||||
rDisclosure.size = MasterFeedTableViewSectionHeaderLayout.disclosureButtonSize
|
||||
rDisclosure.origin.x = bounds.maxX - rDisclosure.size.width
|
||||
|
||||
// Unread Count
|
||||
let unreadCountSize = unreadCountView.contentSize
|
||||
let unreadCountIsHidden = unreadCountView.unreadCount < 1
|
||||
|
||||
var rUnread = CGRect.zero
|
||||
if !unreadCountIsHidden {
|
||||
rUnread.size = unreadCountSize
|
||||
rUnread.origin.x = bounds.maxX - (MasterFeedTableViewSectionHeaderLayout.unreadCountMarginRight + unreadCountSize.width + rDisclosure.size.width)
|
||||
}
|
||||
|
||||
// Max Unread Count
|
||||
// We can't reload Section Headers so we don't let the title extend into the (probably) worse case Unread Count area.
|
||||
let maxUnreadCountView = MasterFeedUnreadCountView(frame: CGRect.zero)
|
||||
maxUnreadCountView.unreadCount = 888
|
||||
let maxUnreadCountSize = maxUnreadCountView.contentSize
|
||||
|
||||
// Title
|
||||
let rLabelx = 15.0
|
||||
let rLabely = UIFontMetrics.default.scaledValue(for: MasterFeedTableViewSectionHeaderLayout.verticalPadding)
|
||||
|
||||
var labelWidth = CGFloat.zero
|
||||
labelWidth = cellWidth - (rLabelx + MasterFeedTableViewSectionHeaderLayout.labelMarginRight + maxUnreadCountSize.width + MasterFeedTableViewSectionHeaderLayout.unreadCountMarginRight)
|
||||
labelWidth = cellWidth - (rLabelx + MasterFeedTableViewSectionHeaderLayout.labelMarginRight)
|
||||
|
||||
let labelSizeInfo = MultilineUILabelSizer.size(for: label.text ?? "", font: label.font, numberOfLines: 0, width: Int(floor(labelWidth)))
|
||||
var rLabel = CGRect(x: rLabelx, y: rLabely, width: labelWidth, height: labelSizeInfo.size.height)
|
||||
|
||||
// Determine cell height
|
||||
let paddedLabelHeight = rLabel.maxY + UIFontMetrics.default.scaledValue(for: MasterFeedTableViewSectionHeaderLayout.verticalPadding)
|
||||
let maxGraphicsHeight = [rUnread, rDisclosure].maxY()
|
||||
let maxGraphicsHeight = [rDisclosure].maxY()
|
||||
var cellHeight = max(paddedLabelHeight, maxGraphicsHeight)
|
||||
if cellHeight < MasterFeedTableViewSectionHeaderLayout.minRowHeight {
|
||||
cellHeight = MasterFeedTableViewSectionHeaderLayout.minRowHeight
|
||||
@@ -69,9 +52,6 @@ struct MasterFeedTableViewSectionHeaderLayout {
|
||||
|
||||
// Center in Cell
|
||||
let newBounds = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.width, height: cellHeight)
|
||||
if !unreadCountIsHidden {
|
||||
rUnread = MasterFeedTableViewCellLayout.centerVertically(rUnread, newBounds)
|
||||
}
|
||||
rDisclosure = MasterFeedTableViewCellLayout.centerVertically(rDisclosure, newBounds)
|
||||
|
||||
// Small fonts need centered if we hit the minimum row height
|
||||
@@ -81,7 +61,6 @@ struct MasterFeedTableViewSectionHeaderLayout {
|
||||
|
||||
// Assign the properties
|
||||
self.height = cellHeight
|
||||
self.unreadCountRect = rUnread
|
||||
self.disclosureButtonRect = rDisclosure
|
||||
self.titleRect = rLabel
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import RSCore
|
||||
import RSTree
|
||||
import SafariServices
|
||||
|
||||
class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||
class MasterFeedViewController: UITableViewController, UndoableCommandRunner, MainControllerIdentifiable {
|
||||
|
||||
@IBOutlet weak var filterButton: UIBarButtonItem!
|
||||
private var refreshProgressView: RefreshProgressView?
|
||||
@@ -23,9 +23,11 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||
}
|
||||
}
|
||||
|
||||
var undoableCommands = [UndoableCommand]()
|
||||
weak var coordinator: SceneCoordinator!
|
||||
var mainControllerIdentifer = MainControllerIdentifier.masterFeed
|
||||
|
||||
weak var coordinator: SceneCoordinator!
|
||||
var undoableCommands = [UndoableCommand]()
|
||||
|
||||
private let keyboardManager = KeyboardManager(type: .sidebar)
|
||||
override var keyCommands: [UIKeyCommand]? {
|
||||
|
||||
@@ -78,6 +80,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
navigationController?.isToolbarHidden = false
|
||||
updateUI()
|
||||
super.viewWillAppear(animated)
|
||||
}
|
||||
|
||||
@@ -27,8 +27,9 @@ struct MasterTimelineCellData {
|
||||
let starred: Bool
|
||||
let numberOfLines: Int
|
||||
let iconSize: IconSize
|
||||
|
||||
init(article: Article, showFeedName: ShowFeedName, feedName: String?, byline: String?, iconImage: IconImage?, showIcon: Bool, featuredImage: UIImage?, numberOfLines: Int, iconSize: IconSize) {
|
||||
let hideSeparator: Bool
|
||||
|
||||
init(article: Article, showFeedName: ShowFeedName, feedName: String?, byline: String?, iconImage: IconImage?, showIcon: Bool, featuredImage: UIImage?, numberOfLines: Int, iconSize: IconSize, hideSeparator: Bool) {
|
||||
|
||||
self.title = ArticleStringFormatter.truncatedTitle(article)
|
||||
self.attributedTitle = ArticleStringFormatter.attributedTruncatedTitle(article)
|
||||
@@ -65,6 +66,7 @@ struct MasterTimelineCellData {
|
||||
self.starred = article.status.starred
|
||||
self.numberOfLines = numberOfLines
|
||||
self.iconSize = iconSize
|
||||
self.hideSeparator = hideSeparator
|
||||
|
||||
}
|
||||
|
||||
@@ -83,6 +85,7 @@ struct MasterTimelineCellData {
|
||||
self.starred = false
|
||||
self.numberOfLines = 0
|
||||
self.iconSize = .medium
|
||||
self.hideSeparator = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -82,7 +82,6 @@ class MasterTimelineTableViewCell: VibrantTableViewCell {
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
|
||||
super.layoutSubviews()
|
||||
|
||||
let layout = updatedLayout(width: bounds.width)
|
||||
@@ -94,8 +93,6 @@ class MasterTimelineTableViewCell: VibrantTableViewCell {
|
||||
setFrame(for: summaryView, rect: layout.summaryRect)
|
||||
feedNameView.setFrameIfNotEqual(layout.feedNameRect)
|
||||
dateView.setFrameIfNotEqual(layout.dateRect)
|
||||
|
||||
separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
|
||||
}
|
||||
|
||||
func setIconImage(_ image: IconImage) {
|
||||
@@ -272,6 +269,14 @@ private extension MasterTimelineTableViewCell {
|
||||
accessibilityLabel = label
|
||||
}
|
||||
|
||||
func updateSeparator() {
|
||||
if cellData?.hideSeparator ?? false {
|
||||
separatorInset = UIEdgeInsets(top: 0, left: bounds.width + 1, bottom: 0, right: 0)
|
||||
} else {
|
||||
separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
|
||||
}
|
||||
}
|
||||
|
||||
func makeIconEmpty() {
|
||||
if iconView.iconImage != nil {
|
||||
iconView.iconImage = nil
|
||||
@@ -305,6 +310,7 @@ private extension MasterTimelineTableViewCell {
|
||||
updateStarView()
|
||||
updateIconImage()
|
||||
updateAccessiblityLabel()
|
||||
updateSeparator()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,22 +11,25 @@ import RSCore
|
||||
import Account
|
||||
import Articles
|
||||
|
||||
class MasterTimelineViewController: UITableViewController, UndoableCommandRunner {
|
||||
class MasterTimelineViewController: UITableViewController, UndoableCommandRunner, MainControllerIdentifiable {
|
||||
|
||||
private var numberOfTextLines = 0
|
||||
private var iconSize = IconSize.medium
|
||||
private lazy var feedTapGestureRecognizer = UITapGestureRecognizer(target: self, action:#selector(showFeedInspector(_:)))
|
||||
|
||||
private var refreshProgressView: RefreshProgressView?
|
||||
private var filterButton: UIBarButtonItem!
|
||||
|
||||
@IBOutlet weak var markAllAsReadButton: UIBarButtonItem!
|
||||
|
||||
private var filterButton: UIBarButtonItem!
|
||||
private var refreshProgressView: RefreshProgressView!
|
||||
private var refreshProgressItemButton: UIBarButtonItem!
|
||||
private var firstUnreadButton: UIBarButtonItem!
|
||||
|
||||
private lazy var dataSource = makeDataSource()
|
||||
private let searchController = UISearchController(searchResultsController: nil)
|
||||
|
||||
var mainControllerIdentifer = MainControllerIdentifier.masterTimeline
|
||||
|
||||
weak var coordinator: SceneCoordinator!
|
||||
var undoableCommands = [UndoableCommand]()
|
||||
let scrollPositionQueue = CoalescingQueue(name: "Timeline Scroll Position", interval: 0.3, maxInterval: 1.0)
|
||||
@@ -79,6 +82,9 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||
|
||||
// Configure the table
|
||||
tableView.dataSource = dataSource
|
||||
if #available(iOS 15.0, *) {
|
||||
tableView.isPrefetchingEnabled = false
|
||||
}
|
||||
numberOfTextLines = AppDefaults.shared.timelineNumberOfLines
|
||||
iconSize = AppDefaults.shared.timelineIconSize
|
||||
resetEstimatedRowHeight()
|
||||
@@ -89,8 +95,13 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||
|
||||
refreshControl = UIRefreshControl()
|
||||
refreshControl!.addTarget(self, action: #selector(refreshAccounts(_:)), for: .valueChanged)
|
||||
|
||||
|
||||
configureToolbar()
|
||||
|
||||
refreshProgressView = Bundle.main.loadNibNamed("RefreshProgressView", owner: self, options: nil)?[0] as? RefreshProgressView
|
||||
refreshProgressItemButton = UIBarButtonItem(customView: refreshProgressView!)
|
||||
|
||||
|
||||
resetUI(resetScroll: true)
|
||||
|
||||
// Load the table and then scroll to the saved position if available
|
||||
@@ -109,15 +120,18 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
navigationController?.isToolbarHidden = false
|
||||
|
||||
// If the nav bar is hidden, fade it in to avoid it showing stuff as it is getting laid out
|
||||
if navigationController?.navigationBar.isHidden ?? false {
|
||||
navigationController?.navigationBar.alpha = 0
|
||||
}
|
||||
|
||||
super.viewWillAppear(animated)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(true)
|
||||
coordinator.isTimelineViewControllerPending = false
|
||||
|
||||
if navigationController?.navigationBar.alpha == 0 {
|
||||
UIView.animate(withDuration: 0.5) {
|
||||
@@ -436,7 +450,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||
for article in visibleUpdatedArticles {
|
||||
if let indexPath = dataSource.indexPath(for: article) {
|
||||
if let cell = tableView.cellForRow(at: indexPath) as? MasterTimelineTableViewCell {
|
||||
configure(cell, article: article)
|
||||
configure(cell, article: article, indexPath: indexPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -523,7 +537,8 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||
}
|
||||
|
||||
@objc private func reloadAllVisibleCells() {
|
||||
reconfigureCells(coordinator.articles)
|
||||
let visibleArticles = tableView.indexPathsForVisibleRows!.compactMap { return dataSource.itemIdentifier(for: $0) }
|
||||
reloadCells(visibleArticles)
|
||||
}
|
||||
|
||||
private func reloadCells(_ articles: [Article]) {
|
||||
@@ -534,15 +549,6 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||
}
|
||||
}
|
||||
|
||||
private func reconfigureCells(_ articles: [Article]) {
|
||||
guard #available(iOS 15, *) else { return }
|
||||
var snapshot = dataSource.snapshot()
|
||||
snapshot.reconfigureItems(articles)
|
||||
dataSource.apply(snapshot, animatingDifferences: false) { [weak self] in
|
||||
self?.restoreSelectionIfNecessary(adjustScroll: false)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Cell Configuring
|
||||
|
||||
private func resetEstimatedRowHeight() {
|
||||
@@ -553,7 +559,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||
let status = ArticleStatus(articleID: prototypeID, read: false, starred: false, dateArrived: Date())
|
||||
let prototypeArticle = Article(accountID: prototypeID, articleID: prototypeID, webFeedID: prototypeID, uniqueID: prototypeID, title: longTitle, contentHTML: nil, contentText: nil, url: nil, externalURL: nil, summary: nil, imageURL: nil, datePublished: nil, dateModified: nil, authors: nil, status: status)
|
||||
|
||||
let prototypeCellData = MasterTimelineCellData(article: prototypeArticle, showFeedName: .feed, feedName: "Prototype Feed Name", byline: nil, iconImage: nil, showIcon: false, featuredImage: nil, numberOfLines: numberOfTextLines, iconSize: iconSize)
|
||||
let prototypeCellData = MasterTimelineCellData(article: prototypeArticle, showFeedName: .feed, feedName: "Prototype Feed Name", byline: nil, iconImage: nil, showIcon: false, featuredImage: nil, numberOfLines: numberOfTextLines, iconSize: iconSize, hideSeparator: false)
|
||||
|
||||
if UIApplication.shared.preferredContentSizeCategory.isAccessibilityCategory {
|
||||
let layout = MasterTimelineAccessibilityCellLayout(width: tableView.bounds.width, insets: tableView.safeAreaInsets, cellData: prototypeCellData)
|
||||
@@ -603,9 +609,8 @@ extension MasterTimelineViewController: UISearchBarDelegate {
|
||||
|
||||
private extension MasterTimelineViewController {
|
||||
|
||||
func configureToolbar() {
|
||||
|
||||
guard !coordinator.isThreePanelMode else {
|
||||
func configureToolbar() {
|
||||
guard splitViewController?.isCollapsed ?? true else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -679,16 +684,9 @@ private extension MasterTimelineViewController {
|
||||
firstUnreadButton.isEnabled = coordinator.isTimelineUnreadAvailable
|
||||
|
||||
if coordinator.isRootSplitCollapsed {
|
||||
if let toolbarItems = toolbarItems, toolbarItems.last != firstUnreadButton {
|
||||
var items = toolbarItems
|
||||
items.append(firstUnreadButton)
|
||||
setToolbarItems(items, animated: false)
|
||||
}
|
||||
setToolbarItems([markAllAsReadButton, .flexibleSpace(), refreshProgressItemButton, .flexibleSpace(), firstUnreadButton], animated: false)
|
||||
} else {
|
||||
if let toolbarItems = toolbarItems, toolbarItems.last == firstUnreadButton {
|
||||
let items = Array(toolbarItems[0..<toolbarItems.count - 1])
|
||||
setToolbarItems(items, animated: false)
|
||||
}
|
||||
setToolbarItems([markAllAsReadButton, .flexibleSpace()], animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,22 +717,23 @@ private extension MasterTimelineViewController {
|
||||
let dataSource: UITableViewDiffableDataSource<Int, Article> =
|
||||
MasterTimelineDataSource(tableView: tableView, cellProvider: { [weak self] tableView, indexPath, article in
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! MasterTimelineTableViewCell
|
||||
self?.configure(cell, article: article)
|
||||
self?.configure(cell, article: article, indexPath: indexPath)
|
||||
return cell
|
||||
})
|
||||
dataSource.defaultRowAnimation = .middle
|
||||
return dataSource
|
||||
}
|
||||
|
||||
func configure(_ cell: MasterTimelineTableViewCell, article: Article) {
|
||||
func configure(_ cell: MasterTimelineTableViewCell, article: Article, indexPath: IndexPath) {
|
||||
|
||||
let iconImage = iconImageFor(article)
|
||||
let featuredImage = featuredImageFor(article)
|
||||
|
||||
let showFeedNames = coordinator.showFeedNames
|
||||
let showIcon = coordinator.showIcons && iconImage != nil
|
||||
cell.cellData = MasterTimelineCellData(article: article, showFeedName: showFeedNames, feedName: article.webFeed?.nameForDisplay, byline: article.byline(), iconImage: iconImage, showIcon: showIcon, featuredImage: featuredImage, numberOfLines: numberOfTextLines, iconSize: iconSize)
|
||||
let hideSeparater = indexPath.row == coordinator.articles.count - 1
|
||||
|
||||
cell.cellData = MasterTimelineCellData(article: article, showFeedName: showFeedNames, feedName: article.webFeed?.nameForDisplay, byline: article.byline(), iconImage: iconImage, showIcon: showIcon, featuredImage: featuredImage, numberOfLines: numberOfTextLines, iconSize: iconSize, hideSeparator: hideSeparater)
|
||||
}
|
||||
|
||||
func iconImageFor(_ article: Article) -> IconImage? {
|
||||
|
||||
@@ -204,6 +204,8 @@
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>NetNewsWire Theme</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
|
||||
@@ -25,11 +25,6 @@ class RootSplitViewController: UISplitViewController {
|
||||
coordinator.resetFocus()
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
self.coordinator.configurePanelMode(for: size)
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
}
|
||||
|
||||
// MARK: Keyboard Shortcuts
|
||||
|
||||
@objc func scrollOrGoToNextUnread(_ sender: Any?) {
|
||||
|
||||
@@ -14,10 +14,15 @@ import RSCore
|
||||
import RSTree
|
||||
import SafariServices
|
||||
|
||||
enum PanelMode {
|
||||
case unset
|
||||
case three
|
||||
case standard
|
||||
protocol MainControllerIdentifiable {
|
||||
var mainControllerIdentifer: MainControllerIdentifier { get }
|
||||
}
|
||||
|
||||
enum MainControllerIdentifier {
|
||||
case none
|
||||
case masterFeed
|
||||
case masterTimeline
|
||||
case article
|
||||
}
|
||||
|
||||
enum SearchScope: Int {
|
||||
@@ -54,34 +59,15 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
|
||||
lazy var webViewProvider = WebViewProvider(coordinator: self)
|
||||
|
||||
private var panelMode: PanelMode = .unset
|
||||
|
||||
private var activityManager = ActivityManager()
|
||||
|
||||
private var rootSplitViewController: RootSplitViewController!
|
||||
private var masterNavigationController: UINavigationController!
|
||||
private var masterFeedViewController: MasterFeedViewController!
|
||||
private var masterTimelineViewController: MasterTimelineViewController?
|
||||
private var subSplitViewController: UISplitViewController?
|
||||
|
||||
private var articleViewController: ArticleViewController? {
|
||||
if let detail = masterNavigationController.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 var articleViewController: ArticleViewController?
|
||||
|
||||
private var lastMainControllerToAppear = MainControllerIdentifier.none
|
||||
|
||||
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 var fetchSerialNumber = 0
|
||||
@@ -98,9 +84,6 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
private var savedSearchArticles: ArticleArray? = nil
|
||||
private var savedSearchArticleIds: Set<String>? = nil
|
||||
|
||||
var isTimelineViewControllerPending = false
|
||||
var isArticleViewControllerPending = false
|
||||
|
||||
private(set) var sortDirection = AppDefaults.shared.timelineSortDirection {
|
||||
didSet {
|
||||
if sortDirection != oldValue {
|
||||
@@ -140,10 +123,6 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
return rootSplitViewController.isCollapsed
|
||||
}
|
||||
|
||||
var isThreePanelMode: Bool {
|
||||
return panelMode == .three
|
||||
}
|
||||
|
||||
var isReadFeedsFiltered: Bool {
|
||||
return treeControllerDelegate.isReadFiltered
|
||||
}
|
||||
@@ -294,11 +273,32 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
|
||||
var timelineUnreadCount: Int = 0
|
||||
|
||||
override init() {
|
||||
treeController = TreeController(delegate: treeControllerDelegate)
|
||||
init(rootSplitViewController: RootSplitViewController) {
|
||||
self.rootSplitViewController = rootSplitViewController
|
||||
self.treeController = TreeController(delegate: treeControllerDelegate)
|
||||
|
||||
super.init()
|
||||
|
||||
|
||||
self.masterFeedViewController = rootSplitViewController.viewController(for: .primary) as? MasterFeedViewController
|
||||
self.masterFeedViewController.coordinator = self
|
||||
if let navController = self.masterFeedViewController?.navigationController {
|
||||
navController.delegate = self
|
||||
configureNavigationController(navController)
|
||||
}
|
||||
|
||||
self.masterTimelineViewController = rootSplitViewController.viewController(for: .supplementary) as? MasterTimelineViewController
|
||||
self.masterTimelineViewController?.coordinator = self
|
||||
if let navController = self.masterTimelineViewController?.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 {
|
||||
markExpanded(sectionNode)
|
||||
shadowTable.append((sectionID: "", feedNodes: [FeedNode]()))
|
||||
@@ -321,30 +321,6 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
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
|
||||
|
||||
masterNavigationController = (rootSplitViewController.viewControllers.first as! UINavigationController)
|
||||
masterNavigationController.delegate = self
|
||||
|
||||
masterFeedViewController = UIStoryboard.main.instantiateController(ofType: MasterFeedViewController.self)
|
||||
masterFeedViewController.coordinator = self
|
||||
masterNavigationController.pushViewController(masterFeedViewController, 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?) {
|
||||
if let activity = activity, let windowState = activity.userInfo?[UserInfoKey.windowState] as? [AnyHashable: Any] {
|
||||
|
||||
@@ -399,26 +375,6 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
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() {
|
||||
if currentArticle != nil {
|
||||
masterTimelineViewController?.focus()
|
||||
@@ -438,7 +394,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
|
||||
func showSearch() {
|
||||
selectFeed(indexPath: nil) {
|
||||
self.installTimelineControllerIfNecessary(animated: false)
|
||||
self.rootSplitViewController.show(.supplementary)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now()) {
|
||||
self.masterTimelineViewController!.showSearchAll()
|
||||
}
|
||||
@@ -793,7 +749,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
if let ip = indexPath, let node = nodeFor(ip), let feed = node.representedObject as? Feed {
|
||||
|
||||
self.activityManager.selecting(feed: feed)
|
||||
self.installTimelineControllerIfNecessary(animated: animations.contains(.navigation))
|
||||
self.rootSplitViewController.show(.supplementary)
|
||||
setTimelineFeed(feed, animated: false) {
|
||||
if self.isReadFeedsFiltered {
|
||||
self.rebuildBackingStores()
|
||||
@@ -808,9 +764,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
self.rebuildBackingStores()
|
||||
}
|
||||
self.activityManager.invalidateSelecting()
|
||||
if self.rootSplitViewController.isCollapsed && self.navControllerForTimeline().viewControllers.last is MasterTimelineViewController {
|
||||
self.navControllerForTimeline().popViewController(animated: animations.contains(.navigation))
|
||||
}
|
||||
self.rootSplitViewController.show(.primary)
|
||||
completion?()
|
||||
}
|
||||
|
||||
@@ -858,31 +812,21 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
activityManager.reading(feed: timelineFeed, article: article)
|
||||
|
||||
if article == nil {
|
||||
if rootSplitViewController.isCollapsed {
|
||||
if masterNavigationController.children.last is ArticleViewController {
|
||||
masterNavigationController.popViewController(animated: animations.contains(.navigation))
|
||||
}
|
||||
} else {
|
||||
articleViewController?.article = nil
|
||||
}
|
||||
rootSplitViewController.show(.supplementary)
|
||||
masterTimelineViewController?.updateArticleSelection(animations: animations)
|
||||
articleViewController?.article = nil
|
||||
return
|
||||
}
|
||||
|
||||
let currentArticleViewController: ArticleViewController
|
||||
if articleViewController == nil {
|
||||
currentArticleViewController = installArticleController(animated: animations.contains(.navigation))
|
||||
} else {
|
||||
currentArticleViewController = articleViewController!
|
||||
}
|
||||
rootSplitViewController.show(.secondary)
|
||||
|
||||
// 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)
|
||||
|
||||
masterTimelineViewController?.updateArticleSelection(animations: animations)
|
||||
currentArticleViewController.article = article
|
||||
articleViewController?.article = article
|
||||
if let isShowingExtractedArticle = isShowingExtractedArticle, let articleWindowScrollY = articleWindowScrollY {
|
||||
currentArticleViewController.restoreScrollPosition = (isShowingExtractedArticle, articleWindowScrollY)
|
||||
articleViewController?.restoreScrollPosition = (isShowingExtractedArticle, articleWindowScrollY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1029,7 +973,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
|
||||
func markAllAsReadInTimeline(completion: (() -> Void)? = nil) {
|
||||
markAllAsRead(articles) {
|
||||
self.masterNavigationController.popViewController(animated: true)
|
||||
self.rootSplitViewController.show(.primary)
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
@@ -1339,45 +1283,28 @@ class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
// MARK: UISplitViewControllerDelegate
|
||||
|
||||
extension SceneCoordinator: UISplitViewControllerDelegate {
|
||||
|
||||
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
|
||||
masterTimelineViewController?.updateUI()
|
||||
|
||||
guard !isThreePanelMode else {
|
||||
return true
|
||||
}
|
||||
|
||||
if let articleViewController = (secondaryViewController as? UINavigationController)?.topViewController as? ArticleViewController {
|
||||
if currentArticle != nil {
|
||||
masterNavigationController.pushViewController(articleViewController, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func splitViewController(_ splitViewController: UISplitViewController, separateSecondaryFrom primaryViewController: UIViewController) -> UIViewController? {
|
||||
masterTimelineViewController?.updateUI()
|
||||
|
||||
guard !isThreePanelMode else {
|
||||
return subSplitViewController
|
||||
func splitViewController(_ svc: UISplitViewController, topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column) -> UISplitViewController.Column {
|
||||
switch proposedTopColumn {
|
||||
case .supplementary:
|
||||
if currentFeedIndexPath != nil {
|
||||
return .supplementary
|
||||
} else {
|
||||
return .primary
|
||||
}
|
||||
case .secondary:
|
||||
if currentArticle != nil {
|
||||
return .secondary
|
||||
} else {
|
||||
if currentFeedIndexPath != nil {
|
||||
return .supplementary
|
||||
} else {
|
||||
return .primary
|
||||
}
|
||||
}
|
||||
default:
|
||||
return .primary
|
||||
}
|
||||
|
||||
if let articleViewController = masterNavigationController.viewControllers.last as? ArticleViewController {
|
||||
articleViewController.showBars(self)
|
||||
masterNavigationController.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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1387,13 +1314,24 @@ extension SceneCoordinator: UISplitViewControllerDelegate {
|
||||
extension SceneCoordinator: UINavigationControllerDelegate {
|
||||
|
||||
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
|
||||
guard UIApplication.shared.applicationState != .background else {
|
||||
return
|
||||
}
|
||||
|
||||
if UIApplication.shared.applicationState == .background {
|
||||
guard rootSplitViewController.isCollapsed else {
|
||||
return
|
||||
}
|
||||
|
||||
defer {
|
||||
if let mainController = viewController as? MainControllerIdentifiable {
|
||||
lastMainControllerToAppear = mainController.mainControllerIdentifer
|
||||
} else if let mainController = (viewController as? UINavigationController)?.topViewController as? MainControllerIdentifiable {
|
||||
lastMainControllerToAppear = mainController.mainControllerIdentifer
|
||||
}
|
||||
}
|
||||
|
||||
// If we are showing the Feeds and only the feeds start clearing stuff
|
||||
if viewController === masterFeedViewController && !isThreePanelMode && !isTimelineViewControllerPending {
|
||||
if viewController === masterFeedViewController && lastMainControllerToAppear == .masterTimeline {
|
||||
activityManager.invalidateCurrentActivities()
|
||||
selectFeed(nil, animations: [.scroll, .select, .navigation])
|
||||
return
|
||||
@@ -1403,25 +1341,51 @@ extension SceneCoordinator: UINavigationControllerDelegate {
|
||||
// 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
|
||||
// ArticleViewController will be pushed, but we will briefly show the Timeline. Don't clear things out when that happens.
|
||||
if viewController === masterTimelineViewController && !isThreePanelMode && rootSplitViewController.isCollapsed && !isArticleViewControllerPending {
|
||||
currentArticle = nil
|
||||
masterTimelineViewController?.updateArticleSelection(animations: [.scroll, .select, .navigation])
|
||||
activityManager.invalidateReading()
|
||||
if viewController === masterTimelineViewController && lastMainControllerToAppear == .article {
|
||||
selectArticle(nil)
|
||||
|
||||
// Restore any bars hidden by the article controller
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
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.tintColor = AppAssets.primaryAccentColor
|
||||
}
|
||||
|
||||
func markArticlesWithUndo(_ articles: [Article], statusKey: ArticleStatus.Key, flag: Bool, completion: (() -> Void)? = nil) {
|
||||
guard let undoManager = undoManager,
|
||||
@@ -2077,134 +2041,6 @@ private extension SceneCoordinator {
|
||||
|
||||
}
|
||||
|
||||
// MARK: Three Panel Mode
|
||||
|
||||
func installTimelineControllerIfNecessary(animated: Bool) {
|
||||
if navControllerForTimeline().viewControllers.filter({ $0 is MasterTimelineViewController }).count < 1 {
|
||||
isTimelineViewControllerPending = true
|
||||
masterTimelineViewController = UIStoryboard.main.instantiateController(ofType: MasterTimelineViewController.self)
|
||||
masterTimelineViewController!.coordinator = self
|
||||
navControllerForTimeline().pushViewController(masterTimelineViewController!, 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 {
|
||||
masterNavigationController.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 masterNavigationController
|
||||
}
|
||||
}
|
||||
|
||||
func configureThreePanelMode() {
|
||||
articleViewController?.stopArticleExtractorIfProcessing()
|
||||
let articleViewControllerState = articleViewController?.currentState
|
||||
defer {
|
||||
masterNavigationController.viewControllers = [masterFeedViewController]
|
||||
}
|
||||
|
||||
if rootSplitViewController.viewControllers.last is InteractiveNavigationController {
|
||||
_ = rootSplitViewController.viewControllers.popLast()
|
||||
}
|
||||
|
||||
installSubSplit()
|
||||
installTimelineControllerIfNecessary(animated: false)
|
||||
masterTimelineViewController?.navigationItem.leftBarButtonItem = rootSplitViewController.displayModeButtonItem
|
||||
masterTimelineViewController?.navigationItem.leftItemsSupplementBackButton = true
|
||||
|
||||
installArticleController(state: articleViewControllerState, animated: false)
|
||||
|
||||
masterFeedViewController.restoreSelectionIfNecessary(adjustScroll: true)
|
||||
masterTimelineViewController!.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
|
||||
|
||||
masterNavigationController.viewControllers = [masterFeedViewController]
|
||||
if rootSplitViewController.viewControllers.last is UISplitViewController {
|
||||
subSplitViewController = nil
|
||||
_ = rootSplitViewController.viewControllers.popLast()
|
||||
}
|
||||
|
||||
if currentFeedIndexPath != nil {
|
||||
masterTimelineViewController = UIStoryboard.main.instantiateController(ofType: MasterTimelineViewController.self)
|
||||
masterTimelineViewController!.coordinator = self
|
||||
masterNavigationController.pushViewController(masterTimelineViewController!, animated: false)
|
||||
}
|
||||
|
||||
installArticleController(state: articleViewControllerState, animated: false)
|
||||
}
|
||||
|
||||
// MARK: NSUserActivity
|
||||
|
||||
func windowState() -> [AnyHashable: Any] {
|
||||
|
||||
@@ -14,35 +14,35 @@ import Zip
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
var coordinator = SceneCoordinator()
|
||||
var coordinator: SceneCoordinator!
|
||||
|
||||
// UIWindowScene delegate
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||
|
||||
window = UIWindow(windowScene: scene as! UIWindowScene)
|
||||
window!.tintColor = AppAssets.primaryAccentColor
|
||||
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
|
||||
|
||||
coordinator.restoreWindowState(session.stateRestorationActivity)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange), name: UserDefaults.didChangeNotification, object: nil)
|
||||
|
||||
if let _ = connectionOptions.urlContexts.first?.url {
|
||||
window?.makeKeyAndVisible()
|
||||
self.scene(scene, openURLContexts: connectionOptions.urlContexts)
|
||||
return
|
||||
}
|
||||
|
||||
if let shortcutItem = connectionOptions.shortcutItem {
|
||||
window!.makeKeyAndVisible()
|
||||
handleShortcutItem(shortcutItem)
|
||||
return
|
||||
}
|
||||
|
||||
if let notificationResponse = connectionOptions.notificationResponse {
|
||||
window!.makeKeyAndVisible()
|
||||
coordinator.handle(notificationResponse)
|
||||
return
|
||||
}
|
||||
@@ -50,8 +50,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
if let userActivity = connectionOptions.userActivities.first ?? session.stateRestorationActivity {
|
||||
coordinator.handle(userActivity)
|
||||
}
|
||||
|
||||
window!.makeKeyAndVisible()
|
||||
|
||||
}
|
||||
|
||||
func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
|
||||
@@ -74,7 +73,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||
appDelegate.resumeDatabaseProcessingIfNecessary()
|
||||
appDelegate.prepareAccountsForForeground()
|
||||
coordinator.configurePanelMode(for: window!.frame.size)
|
||||
coordinator.resetFocus()
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,12 @@ import UIKit
|
||||
struct ArticleThemeImporter {
|
||||
|
||||
static func importTheme(controller: UIViewController, filename: String) throws {
|
||||
let theme = try ArticleTheme(path: filename)
|
||||
let theme = try ArticleTheme(path: filename, isAppTheme: false)
|
||||
|
||||
let localizedTitleText = NSLocalizedString("Install theme “%@” by %@?", comment: "Theme message text")
|
||||
let title = NSString.localizedStringWithFormat(localizedTitleText as NSString, theme.name, theme.creatorName) as String
|
||||
|
||||
let localizedMessageText = NSLocalizedString("Author's Website:\n%@", comment: "Authors website")
|
||||
let localizedMessageText = NSLocalizedString("Author‘s website:\n%@", comment: "Authors website")
|
||||
let message = NSString.localizedStringWithFormat(localizedMessageText as NSString, theme.creatorHomePage) as String
|
||||
|
||||
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
|
||||
@@ -71,9 +71,10 @@ class ArticleThemesTableViewController: UITableViewController {
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
||||
guard indexPath.row != 0,
|
||||
let cell = tableView.cellForRow(at: indexPath),
|
||||
let themeName = cell.textLabel?.text else { return nil }
|
||||
guard let cell = tableView.cellForRow(at: indexPath),
|
||||
let themeName = cell.textLabel?.text,
|
||||
let theme = ArticleThemesManager.shared.articleThemeWithThemeName(themeName),
|
||||
!theme.isAppTheme else { return nil }
|
||||
|
||||
let deleteTitle = NSLocalizedString("Delete", comment: "Delete")
|
||||
let deleteAction = UIContextualAction(style: .normal, title: deleteTitle) { [weak self] (action, view, completion) in
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
<tableViewSection headerTitle="Notifications, Badge, Data, & More" id="Bmb-Oi-RZK">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="F9H-Kr-npj" style="IBUITableViewCellStyleDefault" id="zvg-7C-BlH" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="49.5" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="49.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="zvg-7C-BlH" id="Tqk-Tu-E6K">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Open System Settings" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="F9H-Kr-npj">
|
||||
<rect key="frame" x="20" y="0.0" width="334" height="43.5"/>
|
||||
<rect key="frame" x="20" y="0.0" width="334" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
@@ -37,10 +37,10 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="l7X-8L-61m" style="IBUITableViewCellStyleDefault" id="xdC-t4-59D" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="93" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="93.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="xdC-t4-59D" id="aDf-bJ-EHn">
|
||||
<rect key="frame" x="0.0" y="0.0" width="345.5" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="345.5" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="New Article Notifications" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="l7X-8L-61m">
|
||||
@@ -58,14 +58,14 @@
|
||||
<tableViewSection headerTitle="Accounts" id="0ac-Ze-Dh4">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="6sn-wY-hHH" style="IBUITableViewCellStyleDefault" id="XHc-rQ-7FK" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="186.5" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="187.5" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="XHc-rQ-7FK" id="nmL-EM-Bsi">
|
||||
<rect key="frame" x="0.0" y="0.0" width="345.5" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="345.5" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Add Account" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="6sn-wY-hHH">
|
||||
<rect key="frame" x="20" y="0.0" width="317.5" height="43.5"/>
|
||||
<rect key="frame" x="20" y="0.0" width="317.5" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
@@ -79,7 +79,7 @@
|
||||
<tableViewSection headerTitle="Extensions" id="oRB-NZ-WpG">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="QFr-Rs-eW2" style="IBUITableViewCellStyleDefault" id="6QJ-fX-278" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="280" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="281.5" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="6QJ-fX-278" id="PDs-8c-XUa">
|
||||
<rect key="frame" x="0.0" y="0.0" width="345.5" height="43.5"/>
|
||||
@@ -100,7 +100,7 @@
|
||||
<tableViewSection headerTitle="Feeds" id="hAC-uA-RbS">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="4Hg-B3-zAE" style="IBUITableViewCellStyleDefault" id="glf-Pg-s3P" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="373.5" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="375" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="glf-Pg-s3P" id="bPA-43-Oqh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
@@ -117,7 +117,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="25J-iX-3at" style="IBUITableViewCellStyleDefault" id="qke-Ha-PXl" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="417" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="418.5" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="qke-Ha-PXl" id="pZi-ck-RV5">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
@@ -134,7 +134,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="dXN-Mw-yf2" style="IBUITableViewCellStyleDefault" id="F0L-Ut-reX" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="460.5" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="462" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="F0L-Ut-reX" id="5SX-M2-2jR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
@@ -155,7 +155,7 @@
|
||||
<tableViewSection headerTitle="Timeline" id="9Pk-Y8-JVJ">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="MpA-w1-Wwh" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="554" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="555.5" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="MpA-w1-Wwh" id="GhU-ib-Mz8">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
@@ -188,7 +188,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="f7r-AZ-aDn" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="597.5" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="599" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="f7r-AZ-aDn" id="KHC-cc-tOC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
@@ -221,7 +221,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="5wo-fM-0l6" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="641" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="642.5" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="5wo-fM-0l6" id="XAn-lK-LoN">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
@@ -254,7 +254,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="8Gj-qz-NMY" customClass="VibrantBasicTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="684.5" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="686" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="8Gj-qz-NMY" id="OTe-tG-sb4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="345.5" height="43.5"/>
|
||||
@@ -281,7 +281,7 @@
|
||||
<tableViewSection headerTitle="Articles" id="TRr-Ew-IvU">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="WFP-zj-Pve" customClass="VibrantBasicTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="778" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="779.5" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="WFP-zj-Pve" id="DCX-wc-LSo">
|
||||
<rect key="frame" x="0.0" y="0.0" width="345.5" height="43.5"/>
|
||||
@@ -313,14 +313,14 @@
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="SXs-NQ-y3U" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="821.5" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="823" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="SXs-NQ-y3U" id="BpI-Hz-KH2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="Confirm Mark All as Read" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5tY-5k-v2g">
|
||||
<rect key="frame" x="20" y="11" width="163" height="22"/>
|
||||
<rect key="frame" x="20" y="11" width="163" height="21.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@@ -346,20 +346,20 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="EYf-v1-lNi" customClass="VibrantBasicTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="865.5" width="374" height="44"/>
|
||||
<rect key="frame" x="20" y="866.5" width="374" height="44.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="EYf-v1-lNi" id="7nz-0Y-HaW">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="44.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Open Links in NetNewsWire" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wm4-Y1-7nX">
|
||||
<rect key="frame" x="20" y="14" width="319" height="17.5"/>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Open Links in NetNewsWire" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wm4-Y1-7nX">
|
||||
<rect key="frame" x="20" y="14" width="279" height="17.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dhR-L2-PX3" userLabel="Open Links in NetNewsWire">
|
||||
<rect key="frame" x="347" y="7" width="51" height="31"/>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dhR-L2-PX3" userLabel="Open Links in NetNewsWire">
|
||||
<rect key="frame" x="307" y="7" width="51" height="31"/>
|
||||
<color key="onTintColor" name="primaryAccentColor"/>
|
||||
<connections>
|
||||
<action selector="switchBrowserPreference:" destination="a0p-rk-skQ" eventType="valueChanged" id="hLC-cV-Wjj"/>
|
||||
@@ -379,65 +379,24 @@
|
||||
<outlet property="label" destination="Wm4-Y1-7nX" id="R62-za-yVz"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="WR6-xo-ty2" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="909.5" width="374" height="80.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="WR6-xo-ty2" id="zX8-l2-bVH">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="80.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" verticalCompressionResistancePriority="751" ambiguous="YES" text="Enable Full Screen Articles" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="79e-5s-vd0">
|
||||
<rect key="frame" x="20" y="11" width="172" height="15.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="2Md-2E-7Z4">
|
||||
<rect key="frame" x="347" y="6" width="51" height="31"/>
|
||||
<color key="onTintColor" name="primaryAccentColor"/>
|
||||
<connections>
|
||||
<action selector="switchFullscreenArticles:" destination="a0p-rk-skQ" eventType="valueChanged" id="5fa-Ad-e0j"/>
|
||||
</connections>
|
||||
</switch>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" ambiguous="YES" text="Tap the article top bar to enter Full Screen. Tap the top or bottom to exit." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="a30-nc-ZS4">
|
||||
<rect key="frame" x="20" y="33" width="313" height="0.0"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="2Md-2E-7Z4" firstAttribute="centerY" secondItem="79e-5s-vd0" secondAttribute="centerY" id="3KV-rT-Dfb"/>
|
||||
<constraint firstItem="a30-nc-ZS4" firstAttribute="leading" secondItem="zX8-l2-bVH" secondAttribute="leadingMargin" id="52y-SY-gbp"/>
|
||||
<constraint firstItem="79e-5s-vd0" firstAttribute="top" secondItem="zX8-l2-bVH" secondAttribute="topMargin" id="9bF-Q1-sYE"/>
|
||||
<constraint firstItem="2Md-2E-7Z4" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="a30-nc-ZS4" secondAttribute="trailing" constant="8" id="E9l-8S-WBL"/>
|
||||
<constraint firstAttribute="trailing" secondItem="2Md-2E-7Z4" secondAttribute="trailing" constant="18" id="ELH-06-H2j"/>
|
||||
<constraint firstItem="2Md-2E-7Z4" firstAttribute="top" relation="greaterThanOrEqual" secondItem="zX8-l2-bVH" secondAttribute="top" constant="6" id="aBe-aC-mva"/>
|
||||
<constraint firstItem="a30-nc-ZS4" firstAttribute="bottom" secondItem="zX8-l2-bVH" secondAttribute="bottomMargin" id="b3g-at-rjh"/>
|
||||
<constraint firstItem="2Md-2E-7Z4" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="79e-5s-vd0" secondAttribute="trailing" constant="8" id="lUn-8D-X20"/>
|
||||
<constraint firstItem="79e-5s-vd0" firstAttribute="leading" secondItem="zX8-l2-bVH" secondAttribute="leadingMargin" id="tdZ-30-ACC"/>
|
||||
<constraint firstItem="a30-nc-ZS4" firstAttribute="top" secondItem="79e-5s-vd0" secondAttribute="bottom" constant="6.5" id="wuJ-LG-d6p"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Appearance" id="TkH-4v-yhk">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="EvG-yE-gDF" customClass="VibrantBasicTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1040" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="961" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="EvG-yE-gDF" id="wBN-zJ-6pN">
|
||||
<rect key="frame" x="0.0" y="0.0" width="357.5" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="345.5" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Color Palette" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2Fp-li-dGP">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Color Palette" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2Fp-li-dGP">
|
||||
<rect key="frame" x="20" y="13.5" width="83.5" height="17"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Automatic" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="16m-Ns-Y8V">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Automatic" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="16m-Ns-Y8V">
|
||||
<rect key="frame" x="272" y="13.5" width="65.5" height="17"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
@@ -461,13 +420,13 @@
|
||||
<tableViewSection headerTitle="Help" id="CS8-fJ-ghn">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="uGk-2d-oFc" style="IBUITableViewCellStyleDefault" id="Tle-IV-D40" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1133.5" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="1054.5" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Tle-IV-D40" id="IJD-ZB-8Wm">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" ambiguous="YES" insetsLayoutMarginsFromSafeArea="NO" text="NetNewsWire Help" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="uGk-2d-oFc">
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="NetNewsWire Help" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="uGk-2d-oFc">
|
||||
<rect key="frame" x="8" y="0.0" width="358" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
@@ -478,13 +437,13 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="6G3-yV-Eyh" style="IBUITableViewCellStyleDefault" id="Tbf-fE-nfx" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1177" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="1098" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Tbf-fE-nfx" id="beV-vI-g3r">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" ambiguous="YES" insetsLayoutMarginsFromSafeArea="NO" text="Website" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="6G3-yV-Eyh">
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Website" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="6G3-yV-Eyh">
|
||||
<rect key="frame" x="8" y="0.0" width="358" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
@@ -495,13 +454,13 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="NeD-y8-KrM" style="IBUITableViewCellStyleDefault" id="TIX-yK-rC6" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1220.5" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="1141.5" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="TIX-yK-rC6" id="qr8-EN-Ofg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" ambiguous="YES" insetsLayoutMarginsFromSafeArea="NO" text="Release Notes" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="NeD-y8-KrM">
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Release Notes" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="NeD-y8-KrM">
|
||||
<rect key="frame" x="8" y="0.0" width="358" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
@@ -512,7 +471,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="lfL-bQ-sOp" style="IBUITableViewCellStyleDefault" id="mFn-fE-zqa" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1264" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="1185" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="mFn-fE-zqa" id="jTe-mf-MRj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
@@ -529,7 +488,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="DDJ-8P-3YY" style="IBUITableViewCellStyleDefault" id="iGs-ze-4gQ" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1307.5" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="1228.5" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="iGs-ze-4gQ" id="EqZ-rF-N0l">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
@@ -546,7 +505,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="DsV-Qv-X4K" style="IBUITableViewCellStyleDefault" id="taJ-sg-wnU" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1351" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="1272" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="taJ-sg-wnU" id="axB-si-1KM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
@@ -563,7 +522,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="zMz-hU-UYU" style="IBUITableViewCellStyleDefault" id="OXi-cg-ab9" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1394.5" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="1315.5" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="OXi-cg-ab9" id="npR-a0-9wv">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
@@ -580,7 +539,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="T7x-zl-6Yf" style="IBUITableViewCellStyleDefault" id="VpI-0o-3Px" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1438" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="1359" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VpI-0o-3Px" id="xRH-i4-vne">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
|
||||
@@ -597,7 +556,7 @@
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="76A-Ng-kfs" style="IBUITableViewCellStyleDefault" id="jK8-tv-hBD" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="1481.5" width="374" height="43.5"/>
|
||||
<rect key="frame" x="20" y="1402.5" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="jK8-tv-hBD" id="I7Q-GQ-u8Y">
|
||||
<rect key="frame" x="0.0" y="0.0" width="357.5" height="43.5"/>
|
||||
@@ -635,7 +594,6 @@
|
||||
<outlet property="groupByFeedSwitch" destination="JNi-Wz-RbU" id="TwH-Kd-o6N"/>
|
||||
<outlet property="openLinksInNetNewsWire" destination="dhR-L2-PX3" id="z1b-pX-bwG"/>
|
||||
<outlet property="refreshClearsReadArticlesSwitch" destination="duV-CN-JmH" id="xTd-jF-Ei1"/>
|
||||
<outlet property="showFullscreenArticlesSwitch" destination="2Md-2E-7Z4" id="lEN-VP-wEO"/>
|
||||
<outlet property="timelineSortOrderSwitch" destination="Keq-Np-l9O" id="Zm7-HG-r5h"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
|
||||
@@ -81,12 +81,6 @@ class SettingsViewController: UITableViewController {
|
||||
} else {
|
||||
confirmMarkAllAsReadSwitch.isOn = false
|
||||
}
|
||||
|
||||
if AppDefaults.shared.articleFullscreenAvailable {
|
||||
showFullscreenArticlesSwitch.isOn = true
|
||||
} else {
|
||||
showFullscreenArticlesSwitch.isOn = false
|
||||
}
|
||||
|
||||
colorPaletteDetailLabel.text = String(describing: AppDefaults.userInterfaceColorPalette)
|
||||
openLinksInNetNewsWire.isOn = !AppDefaults.shared.useSystemBrowser
|
||||
@@ -144,7 +138,7 @@ class SettingsViewController: UITableViewController {
|
||||
}
|
||||
return defaultNumberOfRows
|
||||
case 5:
|
||||
return traitCollection.userInterfaceIdiom == .phone ? 4 : 3
|
||||
return 3
|
||||
default:
|
||||
return super.tableView(tableView, numberOfRowsInSection: section)
|
||||
}
|
||||
@@ -264,7 +258,7 @@ class SettingsViewController: UITableViewController {
|
||||
case 7:
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
openURL("https://netnewswire.com/help/ios/6.0/en/")
|
||||
openURL("https://netnewswire.com/help/ios/6.1/en/")
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 1:
|
||||
openURL("https://netnewswire.com/")
|
||||
@@ -356,14 +350,6 @@ class SettingsViewController: UITableViewController {
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func switchFullscreenArticles(_ sender: Any) {
|
||||
if showFullscreenArticlesSwitch.isOn {
|
||||
AppDefaults.shared.articleFullscreenAvailable = true
|
||||
} else {
|
||||
AppDefaults.shared.articleFullscreenAvailable = false
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func switchBrowserPreference(_ sender: Any) {
|
||||
if openLinksInNetNewsWire.isOn {
|
||||
AppDefaults.shared.useSystemBrowser = false
|
||||
|
||||
@@ -71,7 +71,7 @@ private extension TimelinePreviewTableViewController {
|
||||
|
||||
let iconImage = IconImage(AppAssets.faviconTemplateImage.withTintColor(AppAssets.secondaryAccentColor))
|
||||
|
||||
return MasterTimelineCellData(article: prototypeArticle, showFeedName: .feed, feedName: "Feed Name", byline: nil, iconImage: iconImage, showIcon: true, featuredImage: nil, numberOfLines: AppDefaults.shared.timelineNumberOfLines, iconSize: AppDefaults.shared.timelineIconSize)
|
||||
return MasterTimelineCellData(article: prototypeArticle, showFeedName: .feed, feedName: "Feed Name", byline: nil, iconImage: iconImage, showIcon: true, featuredImage: nil, numberOfLines: AppDefaults.shared.timelineNumberOfLines, iconSize: AppDefaults.shared.timelineIconSize, hideSeparator: false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
//
|
||||
// InteractiveNavigationController.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 8/22/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class InteractiveNavigationController: UINavigationController {
|
||||
|
||||
private let poppableDelegate = PoppableGestureRecognizerDelegate()
|
||||
|
||||
static func template() -> UINavigationController {
|
||||
let navController = InteractiveNavigationController()
|
||||
navController.configure()
|
||||
return navController
|
||||
}
|
||||
|
||||
static func template(rootViewController: UIViewController) -> UINavigationController {
|
||||
let navController = InteractiveNavigationController(rootViewController: rootViewController)
|
||||
navController.configure()
|
||||
return navController
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
poppableDelegate.navigationController = self
|
||||
interactivePopGestureRecognizer?.delegate = poppableDelegate
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
if traitCollection.userInterfaceStyle != previousTraitCollection?.userInterfaceStyle {
|
||||
configure()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private extension InteractiveNavigationController {
|
||||
|
||||
func configure() {
|
||||
isToolbarHidden = false
|
||||
|
||||
// Standard appearance with system background
|
||||
let standardAppearance = UINavigationBarAppearance()
|
||||
standardAppearance.backgroundColor = .clear
|
||||
standardAppearance.shadowColor = nil
|
||||
|
||||
let scrollEdgeAppearance = UINavigationBarAppearance()
|
||||
scrollEdgeAppearance.backgroundColor = .systemBackground
|
||||
scrollEdgeAppearance.shadowColor = nil
|
||||
|
||||
navigationBar.standardAppearance = standardAppearance
|
||||
navigationBar.scrollEdgeAppearance = scrollEdgeAppearance
|
||||
navigationBar.compactAppearance = standardAppearance
|
||||
navigationBar.compactScrollEdgeAppearance = scrollEdgeAppearance
|
||||
|
||||
|
||||
let toolbarAppearance = UIToolbarAppearance()
|
||||
toolbarAppearance.shadowColor = nil
|
||||
toolbar.standardAppearance = toolbarAppearance
|
||||
toolbar.compactAppearance = nil
|
||||
toolbar.scrollEdgeAppearance = nil
|
||||
toolbar.tintColor = AppAssets.primaryAccentColor
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user