Add customize timeline layout setting

This commit is contained in:
Maurice Parker 2019-11-08 17:16:09 -06:00
parent 3a1f53ce70
commit 0df4498fa3
12 changed files with 393 additions and 52 deletions

View File

@ -11,6 +11,9 @@
49F40DF92335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; };
5108F6B62375E612001ABC45 /* CacheCleaner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5108F6B52375E612001ABC45 /* CacheCleaner.swift */; };
5108F6B72375E612001ABC45 /* CacheCleaner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5108F6B52375E612001ABC45 /* CacheCleaner.swift */; };
5108F6D22375EED2001ABC45 /* TimelineCustomizerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5108F6D12375EED2001ABC45 /* TimelineCustomizerViewController.swift */; };
5108F6D42375EEEF001ABC45 /* TimelinePreviewTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5108F6D32375EEEF001ABC45 /* TimelinePreviewTableViewController.swift */; };
5108F6D623762309001ABC45 /* MasterTimelineIconSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5108F6D523762309001ABC45 /* MasterTimelineIconSize.swift */; };
51102165233A7D6C0007A5F7 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */; };
5110C37D2373A8D100A9C04F /* InspectorIconHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5110C37C2373A8D100A9C04F /* InspectorIconHeaderView.swift */; };
51126DA4225FDE2F00722696 /* RSImage-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */; };
@ -1204,6 +1207,9 @@
/* Begin PBXFileReference section */
49F40DEF2335B71000552BF4 /* newsfoot.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = newsfoot.js; sourceTree = "<group>"; };
5108F6B52375E612001ABC45 /* CacheCleaner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheCleaner.swift; sourceTree = "<group>"; };
5108F6D12375EED2001ABC45 /* TimelineCustomizerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineCustomizerViewController.swift; sourceTree = "<group>"; };
5108F6D32375EEEF001ABC45 /* TimelinePreviewTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinePreviewTableViewController.swift; sourceTree = "<group>"; };
5108F6D523762309001ABC45 /* MasterTimelineIconSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineIconSize.swift; sourceTree = "<group>"; };
51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleExtractorButton.swift; sourceTree = "<group>"; };
5110C37C2373A8D100A9C04F /* InspectorIconHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorIconHeaderView.swift; sourceTree = "<group>"; };
51121AA12265430A00BC0EC1 /* NetNewsWire_iOSapp_target.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSapp_target.xcconfig; sourceTree = "<group>"; };
@ -1804,6 +1810,8 @@
516A091D23609A3600EAE89B /* SettingsAccountTableViewCell.xib */,
516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */,
51A16993235E10D600EB091F /* SettingsViewController.swift */,
5108F6D12375EED2001ABC45 /* TimelineCustomizerViewController.swift */,
5108F6D32375EEEF001ABC45 /* TimelinePreviewTableViewController.swift */,
);
path = Settings;
sourceTree = "<group>";
@ -1894,6 +1902,7 @@
51FD413A2342BD0500880194 /* MasterTimelineUnreadCountView.swift */,
FFD43E372340F320009E5CA3 /* UndoAvailableAlertController.swift */,
51C4526F2265091600C03939 /* Cell */,
5108F6D523762309001ABC45 /* MasterTimelineIconSize.swift */,
);
path = MasterTimeline;
sourceTree = "<group>";
@ -3958,6 +3967,7 @@
51C4528E2265099C00C03939 /* SmartFeedsController.swift in Sources */,
51102165233A7D6C0007A5F7 /* ArticleExtractorButton.swift in Sources */,
5141E7392373C18B0013FF27 /* FeedInspectorViewController.swift in Sources */,
5108F6D42375EEEF001ABC45 /* TimelinePreviewTableViewController.swift in Sources */,
84CAFCA522BC8C08007694F0 /* FetchRequestQueue.swift in Sources */,
51C4529C22650A1000C03939 /* SingleFaviconDownloader.swift in Sources */,
51E595A6228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */,
@ -3991,6 +4001,7 @@
51C45268226508F600C03939 /* MasterFeedUnreadCountView.swift in Sources */,
5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */,
51C4529F22650A1900C03939 /* AuthorAvatarDownloader.swift in Sources */,
5108F6D22375EED2001ABC45 /* TimelineCustomizerViewController.swift in Sources */,
519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */,
51CC9B3E231720B2000E842F /* MasterFeedDataSource.swift in Sources */,
FFD43E412340F488009E5CA3 /* UndoAvailableAlertController.swift in Sources */,
@ -4004,6 +4015,7 @@
51CE1C0B23622007005548FC /* RefreshProgressView.swift in Sources */,
511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */,
51A1699D235E10D700EB091F /* SettingsViewController.swift in Sources */,
5108F6D623762309001ABC45 /* MasterTimelineIconSize.swift in Sources */,
51C45293226509C800C03939 /* StarredFeedDelegate.swift in Sources */,
51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */,
51934CCB230F599B006127BE /* ThemedNavigationController.swift in Sources */,

View File

@ -21,6 +21,7 @@ struct AppDefaults {
static let firstRunDate = "firstRunDate"
static let timelineGroupByFeed = "timelineGroupByFeed"
static let timelineNumberOfLines = "timelineNumberOfLines"
static let timelineIconSize = "timelineIconSize"
static let timelineSortDirection = "timelineSortDirection"
static let displayUndoAvailableTip = "displayUndoAvailableTip"
static let refreshInterval = "refreshInterval"
@ -99,11 +100,22 @@ struct AppDefaults {
}
}
static var timelineIconSize: MasterTimelineIconSize {
get {
let rawValue = AppDefaults.shared.integer(forKey: Key.timelineIconSize)
return MasterTimelineIconSize(rawValue: rawValue) ?? MasterTimelineIconSize.medium
}
set {
AppDefaults.shared.set(newValue.rawValue, forKey: Key.timelineIconSize)
}
}
static func registerDefaults() {
let defaults: [String : Any] = [Key.lastImageCacheFlushDate: Date(),
Key.refreshInterval: RefreshInterval.everyHour.rawValue,
Key.timelineGroupByFeed: false,
Key.timelineNumberOfLines: 2,
Key.timelineIconSize: MasterTimelineIconSize.medium.rawValue,
Key.timelineSortDirection: ComparisonResult.orderedDescending.rawValue,
Key.displayUndoAvailableTip: true]
AppDefaults.shared.register(defaults: defaults)

View File

@ -39,7 +39,7 @@ struct MasterTimelineAccessibilityCellLayout: MasterTimelineCellLayout {
// Icon Image
if cellData.showIcon {
self.iconImageRect = MasterTimelineAccessibilityCellLayout.rectForIconView(currentPoint)
self.iconImageRect = MasterTimelineAccessibilityCellLayout.rectForIconView(currentPoint, iconSize: cellData.iconSize)
currentPoint.y = self.iconImageRect.maxY
} else {
self.iconImageRect = CGRect.zero

View File

@ -22,8 +22,9 @@ struct MasterTimelineCellData {
let read: Bool
let starred: Bool
let numberOfLines: Int
let iconSize: MasterTimelineIconSize
init(article: Article, showFeedName: Bool, feedName: String?, iconImage: IconImage?, showIcon: Bool, featuredImage: UIImage?, numberOfLines: Int) {
init(article: Article, showFeedName: Bool, feedName: String?, iconImage: IconImage?, showIcon: Bool, featuredImage: UIImage?, numberOfLines: Int, iconSize: MasterTimelineIconSize) {
self.title = ArticleStringFormatter.truncatedTitle(article)
self.summary = ArticleStringFormatter.truncatedSummary(article)
@ -46,6 +47,7 @@ struct MasterTimelineCellData {
self.read = article.status.read
self.starred = article.status.starred
self.numberOfLines = numberOfLines
self.iconSize = iconSize
}
@ -61,6 +63,7 @@ struct MasterTimelineCellData {
self.read = true
self.starred = false
self.numberOfLines = 0
self.iconSize = .medium
}
}

View File

@ -42,9 +42,9 @@ extension MasterTimelineCellLayout {
return r
}
static func rectForIconView(_ point: CGPoint) -> CGRect {
static func rectForIconView(_ point: CGPoint, iconSize: MasterTimelineIconSize) -> CGRect {
var r = CGRect.zero
r.size = MasterTimelineDefaultCellLayout.iconImageSize
r.size = iconSize.size
r.origin.x = point.x
r.origin.y = point.y + 4
return r

View File

@ -21,7 +21,6 @@ struct MasterTimelineDefaultCellLayout: MasterTimelineCellLayout {
static let starDimension = CGFloat(integerLiteral: 16)
static let starSize = CGSize(width: MasterTimelineDefaultCellLayout.starDimension, height: MasterTimelineDefaultCellLayout.starDimension)
static let iconImageSize = CGSize(width: 32.0, height: 32.0)
static let iconMarginRight = CGFloat(integerLiteral: 8)
static let iconCornerRadius = CGFloat(integerLiteral: 4)
@ -72,7 +71,7 @@ struct MasterTimelineDefaultCellLayout: MasterTimelineCellLayout {
// Icon Image
if cellData.showIcon {
self.iconImageRect = MasterTimelineDefaultCellLayout.rectForIconView(currentPoint)
self.iconImageRect = MasterTimelineDefaultCellLayout.rectForIconView(currentPoint, iconSize: cellData.iconSize)
currentPoint.x = self.iconImageRect.maxX + MasterTimelineDefaultCellLayout.iconMarginRight
} else {
self.iconImageRect = CGRect.zero

View File

@ -0,0 +1,32 @@
//
// MasterTimelineIconSize.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 11/8/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import Foundation
import CoreGraphics
enum MasterTimelineIconSize: Int, CaseIterable {
case small = 1
case medium = 2
case large = 3
private static let smallDimension = CGFloat(integerLiteral: 24)
private static let mediumDimension = CGFloat(integerLiteral: 36)
private static let largeDimension = CGFloat(integerLiteral: 48)
var size: CGSize {
switch self {
case .small:
return CGSize(width: MasterTimelineIconSize.smallDimension, height: MasterTimelineIconSize.smallDimension)
case .medium:
return CGSize(width: MasterTimelineIconSize.mediumDimension, height: MasterTimelineIconSize.mediumDimension)
case .large:
return CGSize(width: MasterTimelineIconSize.largeDimension, height: MasterTimelineIconSize.largeDimension)
}
}
}

View File

@ -15,6 +15,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
private var titleView: MasterTimelineTitleView?
private var numberOfTextLines = 0
private var iconSize = MasterTimelineIconSize.medium
@IBOutlet weak var markAllAsReadButton: UIBarButtonItem!
@IBOutlet weak var firstUnreadButton: UIBarButtonItem!
@ -63,6 +64,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
// Configure the table
tableView.dataSource = dataSource
numberOfTextLines = AppDefaults.timelineNumberOfLines
iconSize = AppDefaults.timelineIconSize
resetEstimatedRowHeight()
resetUI()
@ -348,8 +350,9 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
}
@objc func userDefaultsDidChange(_ note: Notification) {
if numberOfTextLines != AppDefaults.timelineNumberOfLines {
if numberOfTextLines != AppDefaults.timelineNumberOfLines || iconSize != AppDefaults.timelineIconSize {
numberOfTextLines = AppDefaults.timelineNumberOfLines
iconSize = AppDefaults.timelineIconSize
resetEstimatedRowHeight()
reloadAllVisibleCells()
}
@ -392,7 +395,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
let status = ArticleStatus(articleID: prototypeID, read: false, starred: false, userDeleted: false, dateArrived: Date())
let prototypeArticle = Article(accountID: prototypeID, articleID: prototypeID, feedID: prototypeID, uniqueID: prototypeID, title: longTitle, contentHTML: nil, contentText: nil, url: nil, externalURL: nil, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: nil, dateModified: nil, authors: nil, attachments: nil, status: status)
let prototypeCellData = MasterTimelineCellData(article: prototypeArticle, showFeedName: true, feedName: "Prototype Feed Name", iconImage: nil, showIcon: false, featuredImage: nil, numberOfLines: numberOfTextLines)
let prototypeCellData = MasterTimelineCellData(article: prototypeArticle, showFeedName: true, feedName: "Prototype Feed Name", iconImage: nil, showIcon: false, featuredImage: nil, numberOfLines: numberOfTextLines, iconSize: iconSize)
if UIApplication.shared.preferredContentSizeCategory.isAccessibilityCategory {
let layout = MasterTimelineAccessibilityCellLayout(width: tableView.bounds.width, insets: tableView.safeAreaInsets, cellData: prototypeCellData)
@ -513,7 +516,7 @@ private extension MasterTimelineViewController {
let showFeedNames = coordinator.showFeedNames
let showIcon = coordinator.showIcons && iconImage != nil
cell.cellData = MasterTimelineCellData(article: article, showFeedName: showFeedNames, feedName: article.feed?.nameForDisplay, iconImage: iconImage, showIcon: showIcon, featuredImage: featuredImage, numberOfLines: numberOfTextLines)
cell.cellData = MasterTimelineCellData(article: article, showFeedName: showFeedNames, feedName: article.feed?.nameForDisplay, iconImage: iconImage, showIcon: showIcon, featuredImage: featuredImage, numberOfLines: numberOfTextLines, iconSize: iconSize)
}

View File

@ -4,6 +4,7 @@
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@ -200,34 +201,21 @@
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="5wo-fM-0l6" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="6C6-JQ-lfQ" style="IBUITableViewCellStyleDefault" id="5wo-fM-0l6" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="575.5" width="374" height="44"/>
<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="44"/>
<rect key="frame" x="0.0" y="0.0" width="343" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="250" text="Number of Text Lines:" textAlignment="natural" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="b2T-Uw-ugm" customClass="VibrantLabel" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="11" width="167.5" height="22"/>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Customize Layout" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="6C6-JQ-lfQ">
<rect key="frame" x="20" y="0.0" width="315" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<stepper opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minimumValue="1" maximumValue="6" translatesAutoresizingMaskIntoConstraints="NO" id="sqD-br-anp">
<rect key="frame" x="260" y="6" width="94" height="32"/>
<connections>
<action selector="stepNumberOfTextLines:" destination="a0p-rk-skQ" eventType="valueChanged" id="xFW-tH-ksL"/>
</connections>
</stepper>
</subviews>
<constraints>
<constraint firstItem="sqD-br-anp" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="b2T-Uw-ugm" secondAttribute="trailing" constant="8" id="Fhr-Xk-5q8"/>
<constraint firstAttribute="trailing" secondItem="sqD-br-anp" secondAttribute="trailing" constant="20" symbolic="YES" id="cEN-ac-fvx"/>
<constraint firstItem="sqD-br-anp" firstAttribute="centerY" secondItem="XAn-lK-LoN" secondAttribute="centerY" id="cQF-53-dau"/>
<constraint firstItem="b2T-Uw-ugm" firstAttribute="leading" secondItem="XAn-lK-LoN" secondAttribute="leadingMargin" id="dYb-iM-gqX"/>
<constraint firstItem="b2T-Uw-ugm" firstAttribute="top" secondItem="XAn-lK-LoN" secondAttribute="topMargin" id="oDa-1a-bzG"/>
<constraint firstItem="b2T-Uw-ugm" firstAttribute="bottom" secondItem="XAn-lK-LoN" secondAttribute="bottomMargin" id="tgj-4O-AMi"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
@ -353,8 +341,6 @@
</navigationItem>
<connections>
<outlet property="groupByFeedSwitch" destination="JNi-Wz-RbU" id="TwH-Kd-o6N"/>
<outlet property="numberOfTextLinesLabel" destination="b2T-Uw-ugm" id="IUA-fR-A3U"/>
<outlet property="numberOfTextLinesSteppper" destination="sqD-br-anp" id="naH-11-E0I"/>
<outlet property="timelineSortOrderSwitch" destination="Keq-Np-l9O" id="Zm7-HG-r5h"/>
</connections>
</tableViewController>
@ -502,7 +488,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="NetNewsWire" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UgA-s6-Vvg">
<rect key="frame" x="20" y="11" width="374" height="40"/>
<rect key="frame" x="20" y="11" width="334" height="40"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle0"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@ -524,7 +510,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5fQ-qz-qbW">
<rect key="frame" x="16" y="0.0" width="382" height="62"/>
<rect key="frame" x="16" y="0.0" width="342" height="62"/>
<color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
<attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
@ -550,7 +536,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LiZ-Tv-tqb">
<rect key="frame" x="16" y="0.0" width="382" height="62"/>
<rect key="frame" x="16" y="0.0" width="342" height="62"/>
<color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
<attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
@ -576,7 +562,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YLf-rp-9nE">
<rect key="frame" x="16" y="0.0" width="382" height="62"/>
<rect key="frame" x="16" y="0.0" width="342" height="62"/>
<color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
<attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
@ -602,7 +588,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wTL-xl-1rK">
<rect key="frame" x="16" y="0.0" width="382" height="62"/>
<rect key="frame" x="16" y="0.0" width="342" height="62"/>
<color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
<attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
@ -628,7 +614,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DIp-6a-oPH">
<rect key="frame" x="16" y="0.0" width="382" height="62"/>
<rect key="frame" x="16" y="0.0" width="342" height="62"/>
<color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
<attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
@ -663,6 +649,105 @@
</objects>
<point key="canvasLocation" x="2330" y="151"/>
</scene>
<!--Timeline Layout-->
<scene sceneID="XRu-Jc-bbU">
<objects>
<viewController storyboardIdentifier="TimelineCustomizerViewController" title="Timeline Layout" id="amD-xZ-U3A" customClass="TimelineCustomizerViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="2Fb-t4-5QE">
<rect key="frame" x="0.0" y="0.0" width="414" height="808"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Q4t-3M-goU">
<rect key="frame" x="20" y="56" width="374" height="44"/>
<subviews>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="1" maxValue="3" translatesAutoresizingMaskIntoConstraints="NO" id="AW6-CH-AXP">
<rect key="frame" x="18" y="7" width="338" height="31"/>
<connections>
<action selector="iconSizeChanged:" destination="amD-xZ-U3A" eventType="valueChanged" id="4F9-gj-CJE"/>
</connections>
</slider>
</subviews>
<color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
<constraints>
<constraint firstItem="AW6-CH-AXP" firstAttribute="centerY" secondItem="Q4t-3M-goU" secondAttribute="centerY" id="2tx-UF-RkW"/>
<constraint firstAttribute="trailing" secondItem="AW6-CH-AXP" secondAttribute="trailing" constant="20" symbolic="YES" id="948-8l-hpY"/>
<constraint firstItem="AW6-CH-AXP" firstAttribute="leading" secondItem="Q4t-3M-goU" secondAttribute="leading" constant="20" symbolic="YES" id="9wL-uE-1ow"/>
<constraint firstAttribute="height" constant="44" id="DeI-vy-rvH"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ICON SIZE" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VLq-TU-3d6">
<rect key="frame" x="40" y="32" width="64" height="16"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="NUMBER OF LINES" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mER-Fl-iHI">
<rect key="frame" x="40" y="132" width="116" height="16"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oQi-VX-CMV">
<rect key="frame" x="20" y="156" width="374" height="44"/>
<subviews>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="1" maxValue="6" translatesAutoresizingMaskIntoConstraints="NO" id="AIu-s5-Hvq">
<rect key="frame" x="18" y="7" width="338" height="31"/>
<connections>
<action selector="numberOfLinesChanged:" destination="amD-xZ-U3A" eventType="valueChanged" id="h25-Da-ZsS"/>
</connections>
</slider>
</subviews>
<color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="DUy-3j-ouM"/>
<constraint firstAttribute="trailing" secondItem="AIu-s5-Hvq" secondAttribute="trailing" constant="20" symbolic="YES" id="Gca-aa-lpE"/>
<constraint firstItem="AIu-s5-Hvq" firstAttribute="centerY" secondItem="oQi-VX-CMV" secondAttribute="centerY" id="TEJ-Ft-Sbd"/>
<constraint firstItem="AIu-s5-Hvq" firstAttribute="leading" secondItem="oQi-VX-CMV" secondAttribute="leading" constant="20" symbolic="YES" id="hss-h1-hJx"/>
</constraints>
</view>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ou4-Yv-dXA">
<rect key="frame" x="57" y="232" width="300" height="128"/>
<constraints>
<constraint firstAttribute="width" constant="300" id="BfC-yc-CAa"/>
<constraint firstAttribute="height" constant="128" id="wxz-ZX-8fe"/>
</constraints>
<connections>
<segue destination="vZY-Cd-RuD" kind="embed" id="aiA-vf-fq0"/>
</connections>
</containerView>
</subviews>
<color key="backgroundColor" systemColor="systemGroupedBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="Q4t-3M-goU" firstAttribute="leading" secondItem="KNc-Rk-2DX" secondAttribute="leading" constant="20" id="I4p-hM-gHL"/>
<constraint firstItem="oQi-VX-CMV" firstAttribute="leading" secondItem="KNc-Rk-2DX" secondAttribute="leading" constant="20" id="IWG-G9-AD9"/>
<constraint firstItem="Ou4-Yv-dXA" firstAttribute="centerX" secondItem="2Fb-t4-5QE" secondAttribute="centerX" id="Ivk-nj-TCU"/>
<constraint firstItem="KNc-Rk-2DX" firstAttribute="trailing" secondItem="oQi-VX-CMV" secondAttribute="trailing" constant="20" id="MOh-yf-Geq"/>
<constraint firstItem="Q4t-3M-goU" firstAttribute="top" secondItem="VLq-TU-3d6" secondAttribute="bottom" constant="8" id="jNT-qc-8Jl"/>
<constraint firstItem="Q4t-3M-goU" firstAttribute="leading" secondItem="VLq-TU-3d6" secondAttribute="leading" constant="-20" id="n49-GK-LkO"/>
<constraint firstItem="mER-Fl-iHI" firstAttribute="top" secondItem="Q4t-3M-goU" secondAttribute="bottom" constant="32" id="nph-Vn-tuP"/>
<constraint firstItem="Ou4-Yv-dXA" firstAttribute="top" secondItem="oQi-VX-CMV" secondAttribute="bottom" constant="32" id="v6e-DF-DcX"/>
<constraint firstItem="KNc-Rk-2DX" firstAttribute="trailing" secondItem="Q4t-3M-goU" secondAttribute="trailing" constant="20" id="vor-kD-wNk"/>
<constraint firstItem="oQi-VX-CMV" firstAttribute="top" secondItem="mER-Fl-iHI" secondAttribute="bottom" constant="8" id="xb9-ti-Ix9"/>
<constraint firstItem="VLq-TU-3d6" firstAttribute="top" secondItem="KNc-Rk-2DX" secondAttribute="top" constant="32" id="yHc-GQ-FUD"/>
<constraint firstItem="oQi-VX-CMV" firstAttribute="leading" secondItem="mER-Fl-iHI" secondAttribute="leading" constant="-20" id="yMU-8z-2Ie"/>
</constraints>
<viewLayoutGuide key="safeArea" id="KNc-Rk-2DX"/>
</view>
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" translucent="NO" prompted="NO"/>
<connections>
<outlet property="iconSizeSlider" destination="AW6-CH-AXP" id="F7a-gL-nDH"/>
<outlet property="iconSizeSliderContainerView" destination="Q4t-3M-goU" id="dWJ-oN-ogt"/>
<outlet property="numberOfLinesSlider" destination="AIu-s5-Hvq" id="Hga-5M-ect"/>
<outlet property="numberOfLinesSliderContainerView" destination="oQi-VX-CMV" id="tYs-Il-t90"/>
<outlet property="previewContainerView" destination="Ou4-Yv-dXA" id="8qX-fe-lgj"/>
<outlet property="previewHeightConstraint" destination="wxz-ZX-8fe" id="jFw-Zn-L5i"/>
<outlet property="previewWidthConstraint" destination="BfC-yc-CAa" id="Bc6-qb-ge8"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iNo-Vj-YZx" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2995.6521739130435" y="150.66964285714286"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="Ezn-Ny-zye">
<objects>
@ -679,6 +764,45 @@
</objects>
<point key="canvasLocation" x="-454" y="152"/>
</scene>
<!--Timeline Preview Table View Controller-->
<scene sceneID="k4U-0u-Fgv">
<objects>
<viewController id="vZY-Cd-RuD" customClass="TimelinePreviewTableViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="xMG-o1-VfV">
<rect key="frame" x="0.0" y="0.0" width="300" height="128"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="7do-cs-DNM">
<rect key="frame" x="0.0" y="0.0" width="300" height="128"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="Zaq-yo-L5h" customClass="MasterTimelineTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="300" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Zaq-yo-L5h" id="fD5-nw-z0z">
<rect key="frame" x="0.0" y="0.0" width="300" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
</tableView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="7do-cs-DNM" firstAttribute="top" secondItem="xMG-o1-VfV" secondAttribute="top" id="Z1O-Z1-O7s"/>
<constraint firstAttribute="trailing" secondItem="7do-cs-DNM" secondAttribute="trailing" id="cOE-fA-hi7"/>
<constraint firstAttribute="bottom" secondItem="7do-cs-DNM" secondAttribute="bottom" id="eh9-tn-F3u"/>
<constraint firstItem="7do-cs-DNM" firstAttribute="leading" secondItem="xMG-o1-VfV" secondAttribute="leading" id="kW5-pQ-FCx"/>
</constraints>
</view>
<connections>
<outlet property="tableView" destination="7do-cs-DNM" id="A8q-7w-LCQ"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Oq6-5f-Oa7" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3697" y="151"/>
</scene>
</scenes>
<resources>
<image name="accountFeedbin" width="120" height="102"/>

View File

@ -17,8 +17,6 @@ class SettingsViewController: UITableViewController {
@IBOutlet weak var timelineSortOrderSwitch: UISwitch!
@IBOutlet weak var groupByFeedSwitch: UISwitch!
@IBOutlet weak var numberOfTextLinesLabel: UILabel!
@IBOutlet weak var numberOfTextLinesSteppper: UIStepper!
weak var presentingParentController: UIViewController?
@ -52,10 +50,6 @@ class SettingsViewController: UITableViewController {
groupByFeedSwitch.isOn = false
}
let numberOfTextLines = AppDefaults.timelineNumberOfLines
numberOfTextLinesSteppper.value = Double(numberOfTextLines)
updateNumberOfTextLinesLabel(value: numberOfTextLines)
let buildLabel = NonIntrinsicLabel(frame: CGRect(x: 20.0, y: 0.0, width: 0.0, height: 0.0))
buildLabel.font = UIFont.systemFont(ofSize: 11.0)
buildLabel.textColor = UIColor.gray
@ -167,6 +161,14 @@ class SettingsViewController: UITableViewController {
default:
break
}
case 3:
switch indexPath.row {
case 2:
let timeline = UIStoryboard.settings.instantiateController(ofType: TimelineCustomizerViewController.self)
self.navigationController?.pushViewController(timeline, animated: true)
default:
break
}
case 4:
switch indexPath.row {
case 0:
@ -245,12 +247,6 @@ class SettingsViewController: UITableViewController {
}
}
@IBAction func stepNumberOfTextLines(_ sender: UIStepper) {
let numberOfLines = Int(sender.value)
AppDefaults.timelineNumberOfLines = numberOfLines
updateNumberOfTextLinesLabel(value: numberOfLines)
}
// MARK: Notifications
@objc func contentSizeCategoryDidChange() {
@ -295,11 +291,6 @@ extension SettingsViewController: UIDocumentPickerDelegate {
private extension SettingsViewController {
func updateNumberOfTextLinesLabel(value: Int) {
let localizedText = NSLocalizedString("Number of Text Lines: %d", comment: "Number of Text Lines")
numberOfTextLinesLabel.text = NSString.localizedStringWithFormat(localizedText as NSString, value) as String
}
func addFeed() {
self.dismiss(animated: true)

View File

@ -0,0 +1,88 @@
//
// TimelineCustomizerViewController.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 11/8/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import UIKit
class TimelineCustomizerViewController: UIViewController {
@IBOutlet weak var iconSizeSliderContainerView: UIView!
@IBOutlet weak var iconSizeSlider: UISlider!
@IBOutlet weak var numberOfLinesSliderContainerView: UIView!
@IBOutlet weak var numberOfLinesSlider: UISlider!
@IBOutlet weak var previewWidthConstraint: NSLayoutConstraint!
@IBOutlet weak var previewHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var previewContainerView: UIView!
var previewController: TimelinePreviewTableViewController {
return children.first as! TimelinePreviewTableViewController
}
override func viewDidLoad() {
super.viewDidLoad()
iconSizeSliderContainerView.layer.cornerRadius = 12
numberOfLinesSliderContainerView.layer.cornerRadius = 12
numberOfLinesSlider.value = Float(AppDefaults.timelineNumberOfLines)
iconSizeSlider.value = Float(AppDefaults.timelineIconSize.rawValue)
}
override func viewWillAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updatePreviewBorder()
updatePreview()
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
updatePreviewBorder()
updatePreview()
}
@IBAction func iconSizeChanged(_ sender: Any) {
guard let iconSize = MasterTimelineIconSize(rawValue: Int(iconSizeSlider.value)) else { return }
AppDefaults.timelineIconSize = iconSize
updatePreview()
}
@IBAction func numberOfLinesChanged(_ sender: Any) {
AppDefaults.timelineNumberOfLines = Int(numberOfLinesSlider.value)
updatePreview()
}
}
// MARK: Private
private extension TimelineCustomizerViewController {
func updatePreview() {
let previewWidth: CGFloat = {
if traitCollection.userInterfaceIdiom == .phone {
return view.bounds.width
} else {
return view.bounds.width / 1.5
}
}()
previewWidthConstraint.constant = previewWidth
previewHeightConstraint.constant = previewController.heightFor(width: previewWidth)
previewController.reload()
}
func updatePreviewBorder() {
if traitCollection.userInterfaceStyle == .dark {
previewContainerView.layer.borderColor = UIColor.black.cgColor
previewContainerView.layer.borderWidth = 1
} else {
previewContainerView.layer.borderWidth = 0
}
}
}

View File

@ -0,0 +1,77 @@
//
// TimelinePreviewTableViewController.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 11/8/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import UIKit
import Articles
class TimelinePreviewTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func heightFor(width: CGFloat) -> CGFloat {
if UIApplication.shared.preferredContentSizeCategory.isAccessibilityCategory {
let layout = MasterTimelineAccessibilityCellLayout(width: width, insets: tableView.safeAreaInsets, cellData: prototypeCellData)
return layout.height
} else {
let layout = MasterTimelineDefaultCellLayout(width: width, insets: tableView.safeAreaInsets, cellData: prototypeCellData)
return layout.height
}
}
// MARK: - Table view data source
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! MasterTimelineTableViewCell
cell.cellData = prototypeCellData
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
}
// MARK: API
func reload() {
tableView.reloadData()
}
}
// MARK: Private
private extension TimelinePreviewTableViewController {
var prototypeCellData: MasterTimelineCellData {
let longTitle = "Enim ut tellus elementum sagittis vitae et. Nibh praesent tristique magna sit amet purus gravida quis blandit. Neque volutpat ac tincidunt vitae semper quis lectus nulla. Massa id neque aliquam vestibulum morbi blandit. Ultrices vitae auctor eu augue. Enim eu turpis egestas pretium aenean pharetra magna. Eget gravida cum sociis natoque. Sit amet consectetur adipiscing elit. Auctor eu augue ut lectus arcu bibendum. Maecenas volutpat blandit aliquam etiam erat velit. Ut pharetra sit amet aliquam id diam maecenas ultricies. In hac habitasse platea dictumst quisque sagittis purus sit amet."
let prototypeID = "prototype"
let status = ArticleStatus(articleID: prototypeID, read: false, starred: false, userDeleted: false, dateArrived: Date())
let prototypeArticle = Article(accountID: prototypeID, articleID: prototypeID, feedID: prototypeID, uniqueID: prototypeID, title: longTitle, contentHTML: nil, contentText: nil, url: nil, externalURL: nil, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: nil, dateModified: nil, authors: nil, attachments: nil, status: status)
let iconImage = IconImage(AppAssets.faviconTemplateImage.withTintColor(AppAssets.secondaryAccentColor))
return MasterTimelineCellData(article: prototypeArticle, showFeedName: true, feedName: "Feed Name", iconImage: iconImage, showIcon: true, featuredImage: nil, numberOfLines: AppDefaults.timelineNumberOfLines, iconSize: AppDefaults.timelineIconSize)
}
}