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

View File

@ -21,6 +21,7 @@ struct AppDefaults {
static let firstRunDate = "firstRunDate" static let firstRunDate = "firstRunDate"
static let timelineGroupByFeed = "timelineGroupByFeed" static let timelineGroupByFeed = "timelineGroupByFeed"
static let timelineNumberOfLines = "timelineNumberOfLines" static let timelineNumberOfLines = "timelineNumberOfLines"
static let timelineIconSize = "timelineIconSize"
static let timelineSortDirection = "timelineSortDirection" static let timelineSortDirection = "timelineSortDirection"
static let displayUndoAvailableTip = "displayUndoAvailableTip" static let displayUndoAvailableTip = "displayUndoAvailableTip"
static let refreshInterval = "refreshInterval" 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() { static func registerDefaults() {
let defaults: [String : Any] = [Key.lastImageCacheFlushDate: Date(), let defaults: [String : Any] = [Key.lastImageCacheFlushDate: Date(),
Key.refreshInterval: RefreshInterval.everyHour.rawValue, Key.refreshInterval: RefreshInterval.everyHour.rawValue,
Key.timelineGroupByFeed: false, Key.timelineGroupByFeed: false,
Key.timelineNumberOfLines: 2, Key.timelineNumberOfLines: 2,
Key.timelineIconSize: MasterTimelineIconSize.medium.rawValue,
Key.timelineSortDirection: ComparisonResult.orderedDescending.rawValue, Key.timelineSortDirection: ComparisonResult.orderedDescending.rawValue,
Key.displayUndoAvailableTip: true] Key.displayUndoAvailableTip: true]
AppDefaults.shared.register(defaults: defaults) AppDefaults.shared.register(defaults: defaults)

View File

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

View File

@ -22,8 +22,9 @@ struct MasterTimelineCellData {
let read: Bool let read: Bool
let starred: Bool let starred: Bool
let numberOfLines: Int 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.title = ArticleStringFormatter.truncatedTitle(article)
self.summary = ArticleStringFormatter.truncatedSummary(article) self.summary = ArticleStringFormatter.truncatedSummary(article)
@ -46,6 +47,7 @@ struct MasterTimelineCellData {
self.read = article.status.read self.read = article.status.read
self.starred = article.status.starred self.starred = article.status.starred
self.numberOfLines = numberOfLines self.numberOfLines = numberOfLines
self.iconSize = iconSize
} }
@ -61,6 +63,7 @@ struct MasterTimelineCellData {
self.read = true self.read = true
self.starred = false self.starred = false
self.numberOfLines = 0 self.numberOfLines = 0
self.iconSize = .medium
} }
} }

View File

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

View File

@ -21,7 +21,6 @@ struct MasterTimelineDefaultCellLayout: MasterTimelineCellLayout {
static let starDimension = CGFloat(integerLiteral: 16) static let starDimension = CGFloat(integerLiteral: 16)
static let starSize = CGSize(width: MasterTimelineDefaultCellLayout.starDimension, height: MasterTimelineDefaultCellLayout.starDimension) 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 iconMarginRight = CGFloat(integerLiteral: 8)
static let iconCornerRadius = CGFloat(integerLiteral: 4) static let iconCornerRadius = CGFloat(integerLiteral: 4)
@ -72,7 +71,7 @@ struct MasterTimelineDefaultCellLayout: MasterTimelineCellLayout {
// Icon Image // Icon Image
if cellData.showIcon { if cellData.showIcon {
self.iconImageRect = MasterTimelineDefaultCellLayout.rectForIconView(currentPoint) self.iconImageRect = MasterTimelineDefaultCellLayout.rectForIconView(currentPoint, iconSize: cellData.iconSize)
currentPoint.x = self.iconImageRect.maxX + MasterTimelineDefaultCellLayout.iconMarginRight currentPoint.x = self.iconImageRect.maxX + MasterTimelineDefaultCellLayout.iconMarginRight
} else { } else {
self.iconImageRect = CGRect.zero 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 titleView: MasterTimelineTitleView?
private var numberOfTextLines = 0 private var numberOfTextLines = 0
private var iconSize = MasterTimelineIconSize.medium
@IBOutlet weak var markAllAsReadButton: UIBarButtonItem! @IBOutlet weak var markAllAsReadButton: UIBarButtonItem!
@IBOutlet weak var firstUnreadButton: UIBarButtonItem! @IBOutlet weak var firstUnreadButton: UIBarButtonItem!
@ -63,6 +64,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
// Configure the table // Configure the table
tableView.dataSource = dataSource tableView.dataSource = dataSource
numberOfTextLines = AppDefaults.timelineNumberOfLines numberOfTextLines = AppDefaults.timelineNumberOfLines
iconSize = AppDefaults.timelineIconSize
resetEstimatedRowHeight() resetEstimatedRowHeight()
resetUI() resetUI()
@ -348,8 +350,9 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
} }
@objc func userDefaultsDidChange(_ note: Notification) { @objc func userDefaultsDidChange(_ note: Notification) {
if numberOfTextLines != AppDefaults.timelineNumberOfLines { if numberOfTextLines != AppDefaults.timelineNumberOfLines || iconSize != AppDefaults.timelineIconSize {
numberOfTextLines = AppDefaults.timelineNumberOfLines numberOfTextLines = AppDefaults.timelineNumberOfLines
iconSize = AppDefaults.timelineIconSize
resetEstimatedRowHeight() resetEstimatedRowHeight()
reloadAllVisibleCells() reloadAllVisibleCells()
} }
@ -392,7 +395,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
let status = ArticleStatus(articleID: prototypeID, read: false, starred: false, userDeleted: false, dateArrived: Date()) 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 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 { if UIApplication.shared.preferredContentSizeCategory.isAccessibilityCategory {
let layout = MasterTimelineAccessibilityCellLayout(width: tableView.bounds.width, insets: tableView.safeAreaInsets, cellData: prototypeCellData) let layout = MasterTimelineAccessibilityCellLayout(width: tableView.bounds.width, insets: tableView.safeAreaInsets, cellData: prototypeCellData)
@ -513,7 +516,7 @@ private extension MasterTimelineViewController {
let showFeedNames = coordinator.showFeedNames let showFeedNames = coordinator.showFeedNames
let showIcon = coordinator.showIcons && iconImage != nil 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> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/>
<capability name="Named colors" minToolsVersion="9.0"/> <capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
@ -200,34 +201,21 @@
</constraints> </constraints>
</tableViewCellContentView> </tableViewCellContentView>
</tableViewCell> </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"/> <rect key="frame" x="20" y="575.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/> <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"> <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"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <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"> <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="11" width="167.5" height="22"/> <rect key="frame" x="20" y="0.0" width="315" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </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> </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> </tableViewCellContentView>
</tableViewCell> </tableViewCell>
</cells> </cells>
@ -353,8 +341,6 @@
</navigationItem> </navigationItem>
<connections> <connections>
<outlet property="groupByFeedSwitch" destination="JNi-Wz-RbU" id="TwH-Kd-o6N"/> <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"/> <outlet property="timelineSortOrderSwitch" destination="Keq-Np-l9O" id="Zm7-HG-r5h"/>
</connections> </connections>
</tableViewController> </tableViewController>
@ -502,7 +488,7 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <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"> <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"/> <fontDescription key="fontDescription" style="UICTFontTextStyleTitle0"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@ -524,7 +510,7 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5fQ-qz-qbW"> <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"/> <color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
<attributedString key="attributedText"/> <attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
@ -550,7 +536,7 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LiZ-Tv-tqb"> <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"/> <color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
<attributedString key="attributedText"/> <attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
@ -576,7 +562,7 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YLf-rp-9nE"> <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"/> <color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
<attributedString key="attributedText"/> <attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
@ -602,7 +588,7 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wTL-xl-1rK"> <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"/> <color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
<attributedString key="attributedText"/> <attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
@ -628,7 +614,7 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DIp-6a-oPH"> <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"/> <color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
<attributedString key="attributedText"/> <attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
@ -663,6 +649,105 @@
</objects> </objects>
<point key="canvasLocation" x="2330" y="151"/> <point key="canvasLocation" x="2330" y="151"/>
</scene> </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--> <!--Navigation Controller-->
<scene sceneID="Ezn-Ny-zye"> <scene sceneID="Ezn-Ny-zye">
<objects> <objects>
@ -679,6 +764,45 @@
</objects> </objects>
<point key="canvasLocation" x="-454" y="152"/> <point key="canvasLocation" x="-454" y="152"/>
</scene> </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> </scenes>
<resources> <resources>
<image name="accountFeedbin" width="120" height="102"/> <image name="accountFeedbin" width="120" height="102"/>

View File

@ -17,8 +17,6 @@ class SettingsViewController: UITableViewController {
@IBOutlet weak var timelineSortOrderSwitch: UISwitch! @IBOutlet weak var timelineSortOrderSwitch: UISwitch!
@IBOutlet weak var groupByFeedSwitch: UISwitch! @IBOutlet weak var groupByFeedSwitch: UISwitch!
@IBOutlet weak var numberOfTextLinesLabel: UILabel!
@IBOutlet weak var numberOfTextLinesSteppper: UIStepper!
weak var presentingParentController: UIViewController? weak var presentingParentController: UIViewController?
@ -52,10 +50,6 @@ class SettingsViewController: UITableViewController {
groupByFeedSwitch.isOn = false 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)) 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.font = UIFont.systemFont(ofSize: 11.0)
buildLabel.textColor = UIColor.gray buildLabel.textColor = UIColor.gray
@ -167,6 +161,14 @@ class SettingsViewController: UITableViewController {
default: default:
break 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: case 4:
switch indexPath.row { switch indexPath.row {
case 0: 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 // MARK: Notifications
@objc func contentSizeCategoryDidChange() { @objc func contentSizeCategoryDidChange() {
@ -295,11 +291,6 @@ extension SettingsViewController: UIDocumentPickerDelegate {
private extension SettingsViewController { 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() { func addFeed() {
self.dismiss(animated: true) 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)
}
}