diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 898bb9eb0..1af18f1d2 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -109,6 +109,7 @@ 51EF0F77227716200050506E /* FaviconGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F76227716200050506E /* FaviconGenerator.swift */; }; 51EF0F79227716380050506E /* ColorHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F78227716380050506E /* ColorHash.swift */; }; 51EF0F7A22771B890050506E /* ColorHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F78227716380050506E /* ColorHash.swift */; }; + 51EF0F7C2277919E0050506E /* TimelineNumberOfLinesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F7B2277919E0050506E /* TimelineNumberOfLinesViewController.swift */; }; 51F85BE5227217D000C787DC /* RefreshIntervalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F85BDB2272162F00C787DC /* RefreshIntervalViewController.swift */; }; 51F85BE7227245FC00C787DC /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F85BE6227245FC00C787DC /* AboutViewController.swift */; }; 51F85BEB22724CB600C787DC /* About.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 51F85BEA22724CB600C787DC /* About.rtf */; }; @@ -667,6 +668,7 @@ 51EC114B2149FE3300B296E3 /* FolderTreeMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FolderTreeMenu.swift; path = AddFeed/FolderTreeMenu.swift; sourceTree = ""; }; 51EF0F76227716200050506E /* FaviconGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconGenerator.swift; sourceTree = ""; }; 51EF0F78227716380050506E /* ColorHash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorHash.swift; sourceTree = ""; }; + 51EF0F7B2277919E0050506E /* TimelineNumberOfLinesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineNumberOfLinesViewController.swift; sourceTree = ""; }; 51F85BDB2272162F00C787DC /* RefreshIntervalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshIntervalViewController.swift; sourceTree = ""; }; 51F85BE6227245FC00C787DC /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; 51F85BEA22724CB600C787DC /* About.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = About.rtf; sourceTree = ""; }; @@ -970,8 +972,9 @@ children = ( 5183CCEC22711DCE0010922C /* Settings.storyboard */, 5183CCEE227125970010922C /* SettingsViewController.swift */, - 51F85BDB2272162F00C787DC /* RefreshIntervalViewController.swift */, 51F85BE6227245FC00C787DC /* AboutViewController.swift */, + 51F85BDB2272162F00C787DC /* RefreshIntervalViewController.swift */, + 51EF0F7B2277919E0050506E /* TimelineNumberOfLinesViewController.swift */, ); path = Settings; sourceTree = ""; @@ -2272,6 +2275,7 @@ 84F3EE1B20DEC97E003FADEB /* HTMLFeedFinder.swift in Sources */, 51C452A222650A1900C03939 /* RSHTMLMetadata+Extension.swift in Sources */, 5183CCE5226F4DFA0010922C /* RefreshInterval.swift in Sources */, + 51EF0F7C2277919E0050506E /* TimelineNumberOfLinesViewController.swift in Sources */, 51C4529D22650A1000C03939 /* FaviconURLFinder.swift in Sources */, 51C45258226508CF00C03939 /* AppAssets.swift in Sources */, 51C4527C2265091600C03939 /* MasterTimelineCellLayout.swift in Sources */, diff --git a/iOS/AppDefaults.swift b/iOS/AppDefaults.swift index db4f458cf..3e40f1a29 100644 --- a/iOS/AppDefaults.swift +++ b/iOS/AppDefaults.swift @@ -15,6 +15,7 @@ struct AppDefaults { static let timelineSortDirection = "timelineSortDirection" static let refreshInterval = "refreshInterval" static let lastRefresh = "lastRefresh" + static let timelineNumberOfLines = "timelineNumberOfLines" } static let isFirstRun: Bool = { @@ -53,8 +54,17 @@ struct AppDefaults { } } + static var timelineNumberOfLines: Int { + get { + return int(for: Key.timelineNumberOfLines) + } + set { + setInt(for: Key.timelineNumberOfLines, newValue) + } + } + static func registerDefaults() { - let defaults: [String : Any] = [Key.timelineSortDirection: ComparisonResult.orderedDescending.rawValue, Key.refreshInterval: RefreshInterval.everyHour.rawValue] + let defaults: [String : Any] = [Key.timelineSortDirection: ComparisonResult.orderedDescending.rawValue, Key.refreshInterval: RefreshInterval.everyHour.rawValue, Key.timelineNumberOfLines: 2] UserDefaults.standard.register(defaults: defaults) } diff --git a/iOS/MasterTimeline/Cell/MasterTimelineCellData.swift b/iOS/MasterTimeline/Cell/MasterTimelineCellData.swift index b5e495b75..7ecdc8c59 100644 --- a/iOS/MasterTimeline/Cell/MasterTimelineCellData.swift +++ b/iOS/MasterTimeline/Cell/MasterTimelineCellData.swift @@ -21,8 +21,9 @@ struct MasterTimelineCellData { let featuredImage: UIImage? // image from within the article let read: Bool let starred: Bool + let numberOfLines: Int - init(article: Article, showFeedName: Bool, feedName: String?, avatar: UIImage?, showAvatar: Bool, featuredImage: UIImage?) { + init(article: Article, showFeedName: Bool, feedName: String?, avatar: UIImage?, showAvatar: Bool, featuredImage: UIImage?, numberOfLines: Int) { self.title = TimelineStringFormatter.truncatedTitle(article) self.summary = TimelineStringFormatter.truncatedSummary(article) @@ -44,6 +45,8 @@ struct MasterTimelineCellData { self.read = article.status.read self.starred = article.status.starred + self.numberOfLines = numberOfLines + } init() { //Empty @@ -57,6 +60,7 @@ struct MasterTimelineCellData { self.featuredImage = nil self.read = true self.starred = false + self.numberOfLines = 0 } } diff --git a/iOS/MasterTimeline/Cell/MasterTimelineCellLayout.swift b/iOS/MasterTimeline/Cell/MasterTimelineCellLayout.swift index 7539920cf..0cecf7ddf 100644 --- a/iOS/MasterTimeline/Cell/MasterTimelineCellLayout.swift +++ b/iOS/MasterTimeline/Cell/MasterTimelineCellLayout.swift @@ -11,7 +11,6 @@ import RSCore struct MasterTimelineCellLayout { - static let maxNumberOfLines = 2 static let cellPadding = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) static let unreadCircleMarginLeft = CGFloat(integerLiteral: 8) @@ -154,7 +153,7 @@ private extension MasterTimelineCellLayout { r.origin = point - let sizeInfo = MultilineUILabelSizer.size(for: cellData.title, font: MasterTimelineCellLayout.titleFont, numberOfLines: MasterTimelineCellLayout.maxNumberOfLines, width: Int(textAreaWidth)) + let sizeInfo = MultilineUILabelSizer.size(for: cellData.title, font: MasterTimelineCellLayout.titleFont, numberOfLines: cellData.numberOfLines, width: Int(textAreaWidth)) r.size.width = textAreaWidth r.size.height = sizeInfo.size.height @@ -168,7 +167,7 @@ private extension MasterTimelineCellLayout { static func rectForSummary(_ cellData: MasterTimelineCellData, _ point: CGPoint, _ textAreaWidth: CGFloat, _ linesUsed: Int) -> CGRect { - let linesLeft = MasterTimelineCellLayout.maxNumberOfLines - linesUsed + let linesLeft = cellData.numberOfLines - linesUsed var r = CGRect.zero if cellData.summary.isEmpty || linesLeft < 1 { diff --git a/iOS/MasterTimeline/MasterTimelineViewController.swift b/iOS/MasterTimeline/MasterTimelineViewController.swift index 5a8f2421a..5ba6add63 100644 --- a/iOS/MasterTimeline/MasterTimelineViewController.swift +++ b/iOS/MasterTimeline/MasterTimelineViewController.swift @@ -14,6 +14,7 @@ import Articles class MasterTimelineViewController: ProgressTableViewController, UndoableCommandRunner { private static var minAvatarDimension: CGFloat = 20.0 + private var numberOfTextLines = 0 @IBOutlet weak var markAllAsReadButton: UIBarButtonItem! @IBOutlet weak var firstUnreadButton: UIBarButtonItem! @@ -35,6 +36,7 @@ class MasterTimelineViewController: ProgressTableViewController, UndoableCommand NotificationCenter.default.addObserver(self, selector: #selector(avatarDidBecomeAvailable(_:)), name: .AvatarDidBecomeAvailable, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(imageDidBecomeAvailable(_:)), name: .ImageDidBecomeAvailable, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(imageDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(articlesReinitialized(_:)), name: .ArticlesReinitialized, object: navState) NotificationCenter.default.addObserver(self, selector: #selector(articleDataDidChange(_:)), name: .ArticleDataDidChange, object: navState) @@ -45,6 +47,8 @@ class MasterTimelineViewController: ProgressTableViewController, UndoableCommand refreshControl = UIRefreshControl() refreshControl!.addTarget(self, action: #selector(refreshAccounts(_:)), for: .valueChanged) + numberOfTextLines = AppDefaults.timelineNumberOfLines + resetUI() } @@ -243,6 +247,13 @@ class MasterTimelineViewController: ProgressTableViewController, UndoableCommand } } + @objc func userDefaultsDidChange(_ note: Notification) { + if numberOfTextLines != AppDefaults.timelineNumberOfLines { + numberOfTextLines = AppDefaults.timelineNumberOfLines + tableView.reloadData() + } + } + @objc func articlesReinitialized(_ note: Notification) { resetUI() } @@ -346,7 +357,7 @@ private extension MasterTimelineViewController { let showFeedNames = navState?.showFeedNames ?? false let showAvatar = navState?.showAvatars ?? false && avatar != nil - cell.cellData = MasterTimelineCellData(article: article, showFeedName: showFeedNames, feedName: article.feed?.nameForDisplay, avatar: avatar, showAvatar: showAvatar, featuredImage: featuredImage) + cell.cellData = MasterTimelineCellData(article: article, showFeedName: showFeedNames, feedName: article.feed?.nameForDisplay, avatar: avatar, showAvatar: showAvatar, featuredImage: featuredImage, numberOfLines: numberOfTextLines) } diff --git a/iOS/Settings/RefreshIntervalViewController.swift b/iOS/Settings/RefreshIntervalViewController.swift index f663210df..4d933edae 100644 --- a/iOS/Settings/RefreshIntervalViewController.swift +++ b/iOS/Settings/RefreshIntervalViewController.swift @@ -11,7 +11,6 @@ import UIKit class RefreshIntervalViewController: UITableViewController { override func numberOfSections(in tableView: UITableView) -> Int { - // #warning Incomplete implementation, return the number of sections return 1 } diff --git a/iOS/Settings/Settings.storyboard b/iOS/Settings/Settings.storyboard index f45b42cf8..259669559 100644 --- a/iOS/Settings/Settings.storyboard +++ b/iOS/Settings/Settings.storyboard @@ -181,12 +181,36 @@ + + + + + + + + + + + + - + @@ -210,7 +234,7 @@ - + @@ -227,7 +251,7 @@ - + @@ -244,7 +268,7 @@ - + @@ -277,6 +301,7 @@ + @@ -284,6 +309,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -312,7 +366,7 @@ - + @@ -492,7 +546,7 @@ - + diff --git a/iOS/Settings/SettingsViewController.swift b/iOS/Settings/SettingsViewController.swift index f73369530..c6f396dc4 100644 --- a/iOS/Settings/SettingsViewController.swift +++ b/iOS/Settings/SettingsViewController.swift @@ -13,6 +13,7 @@ class SettingsViewController: UITableViewController { @IBOutlet weak var refreshIntervalLabel: UILabel! @IBOutlet weak var timelineSortOrderSwitch: UISwitch! + @IBOutlet weak var timelineNumberOfLinesLabel: UILabel! weak var presentingParentController: UIViewController? @@ -33,6 +34,9 @@ class SettingsViewController: UITableViewController { refreshIntervalLabel.text = AppDefaults.refreshInterval.description() + let numberOfLinesText = NSLocalizedString(" lines", comment: "Lines") + timelineNumberOfLinesLabel.text = "\(AppDefaults.timelineNumberOfLines)" + numberOfLinesText + let buildLabel = UILabel(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 @@ -70,6 +74,11 @@ class SettingsViewController: UITableViewController { } case 2: UIApplication.shared.open(URL(string: "https://appcamp4girls.com/contribute/")!, options: [:]) + case 3: + if indexPath.row == 1 { + let timeline = UIStoryboard.settings.instantiateController(ofType: TimelineNumberOfLinesViewController.self) + self.navigationController?.pushViewController(timeline, animated: true) + } case 4: switch indexPath.row { case 0: diff --git a/iOS/Settings/TimelineNumberOfLinesViewController.swift b/iOS/Settings/TimelineNumberOfLinesViewController.swift new file mode 100644 index 000000000..ea0442d8a --- /dev/null +++ b/iOS/Settings/TimelineNumberOfLinesViewController.swift @@ -0,0 +1,49 @@ +// +// TimelineNumberOfLinesViewController.swift +// NetNewsWire-iOS +// +// Created by Maurice Parker on 4/29/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import UIKit + +class TimelineNumberOfLinesViewController: UITableViewController { + + override func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 5 + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + + let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) + + cell.textLabel?.adjustsFontForContentSizeCategory = true + + let bgView = UIView() + bgView.backgroundColor = AppAssets.selectionBackgroundColor + cell.selectedBackgroundView = bgView + + cell.textLabel?.text = "\(2 + indexPath.row)" + NSLocalizedString(" lines", comment: "Lines") + + let numberOfLines = AppDefaults.timelineNumberOfLines + if indexPath.row + 2 == numberOfLines { + cell.accessoryType = .checkmark + } else { + cell.accessoryType = .none + } + + return cell + + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + AppDefaults.timelineNumberOfLines = indexPath.row + 2 + self.navigationController?.popViewController(animated: true) + } + +}