From f703d58b942a9ee3b1726c777214c2a70fa51f0f Mon Sep 17 00:00:00 2001 From: David Sinclair Date: Thu, 30 Mar 2023 10:36:31 -0700 Subject: [PATCH] #1720 (Grid view) - Added support for the Grid layout on iPhone, with one or two columns. Works fairly well. - Bumped up the feed title & date/author font size again. - Now markes as read on scroll when the card is scrolled halfway off the top. --- .../ios/Classes/DetailViewController.swift | 13 ++-- clients/ios/Classes/FeedDetailCardView.swift | 4 +- clients/ios/Classes/FeedDetailGridView.swift | 6 +- .../Classes/FeedDetailObjCViewController.m | 70 +++++++++++-------- clients/ios/Classes/Story.swift | 14 ++-- .../Classes/StoryDetailObjCViewController.m | 6 +- .../ios/Resources/Settings.bundle/Root.plist | 20 ++++++ 7 files changed, 87 insertions(+), 46 deletions(-) diff --git a/clients/ios/Classes/DetailViewController.swift b/clients/ios/Classes/DetailViewController.swift index a321ad44f..a384682a7 100644 --- a/clients/ios/Classes/DetailViewController.swift +++ b/clients/ios/Classes/DetailViewController.swift @@ -139,6 +139,11 @@ class DetailViewController: BaseViewController { } } + /// Returns `true` if the device is an iPhone, otherwise `false`. + @objc var isPhone: Bool { + return UIDevice.current.userInterfaceIdiom == .phone + } + /// Returns `true` if the window is in portrait orientation, otherwise `false`. @objc var isPortraitOrientation: Bool { return view.window?.windowScene?.interfaceOrientation.isPortrait ?? false @@ -258,7 +263,7 @@ class DetailViewController: BaseViewController { /// Moves the story pages controller to a Grid layout cell content (automatically removing it from the previous parent). func prepareStoriesForGridView() { - guard let storyPagesViewController else { + guard !isPhone, let storyPagesViewController else { return } @@ -338,7 +343,7 @@ class DetailViewController: BaseViewController { } private func adjustTopConstraint() { - if UIDevice.current.userInterfaceIdiom != .phone { + if !isPhone { if view.window?.windowScene?.traitCollection.horizontalSizeClass == .compact { topContainerTopConstraint.constant = -50 } else { @@ -388,7 +393,7 @@ private extension DetailViewController { func checkViewControllers() { let isTop = layout == .top - if layout == .left { + if layout == .left || isPhone { if feedDetailViewController != nil { remove(viewController: feedDetailViewController) @@ -459,7 +464,7 @@ private extension DetailViewController { appDelegate.feedDetailViewController.changedLayout() - if layout != .grid { + if layout != .grid || isPhone { moveStoriesToDetail() } } diff --git a/clients/ios/Classes/FeedDetailCardView.swift b/clients/ios/Classes/FeedDetailCardView.swift index a716a19af..2c1228eb9 100644 --- a/clients/ios/Classes/FeedDetailCardView.swift +++ b/clients/ios/Classes/FeedDetailCardView.swift @@ -261,7 +261,7 @@ struct CardContentView: View { .padding(.leading, 24) Text(story.feedName) - .font(font(named: "WhitneySSm-Medium", size: 11)) + .font(font(named: "WhitneySSm-Medium", size: 12)) .lineLimit(1) .foregroundColor(feedColor) } @@ -315,7 +315,7 @@ struct CardContentView: View { Spacer() Text(story.dateAndAuthor) - .font(font(named: "WhitneySSm-Medium", size: 11)) + .font(font(named: "WhitneySSm-Medium", size: 12)) .foregroundColor(dateAndAuthorColor) .padding(.top, 5) } diff --git a/clients/ios/Classes/FeedDetailGridView.swift b/clients/ios/Classes/FeedDetailGridView.swift index eac96a7ce..03af087ea 100644 --- a/clients/ios/Classes/FeedDetailGridView.swift +++ b/clients/ios/Classes/FeedDetailGridView.swift @@ -69,7 +69,7 @@ struct FeedDetailGridView: View { } } - if cache.isGrid { + if cache.isGrid && !cache.isPhone { EmptyView() .id(storyViewID) } else if let story = cache.selected { @@ -134,7 +134,7 @@ struct FeedDetailGridView: View { .onPreferenceChange(CardKey.self) { print("pref change for '\(story.title)': \($0)") - if let value = $0.first, value.frame.minY < 0 { + if let value = $0.first, value.frame.minY < -(value.frame.size.height / 2) { print("pref '\(story.title)': scrolled off the top") feedDetailInteraction.read(story: story) @@ -153,7 +153,7 @@ struct FeedDetailGridView: View { @ViewBuilder func makeStoryView(cache: StoryCache) -> some View { - if cache.isGrid, let story = cache.selected { + if cache.isGrid, !cache.isPhone, let story = cache.selected { StoryView(cache: cache, story: loaded(story: story), interaction: feedDetailInteraction) // .frame(height: storyHeight) } diff --git a/clients/ios/Classes/FeedDetailObjCViewController.m b/clients/ios/Classes/FeedDetailObjCViewController.m index bcc16cd41..4903d9294 100644 --- a/clients/ios/Classes/FeedDetailObjCViewController.m +++ b/clients/ios/Classes/FeedDetailObjCViewController.m @@ -2390,40 +2390,52 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state }]; } - if ([[UIDevice currentDevice] userInterfaceIdiom] != UIUserInterfaceIdiomPhone) { - [appDelegate addSplitControlToMenuController:viewController]; + [appDelegate addSplitControlToMenuController:viewController]; + + NSString *preferenceKey = @"story_titles_position"; + NSArray *titles; + NSArray *values; + + if (appDelegate.detailViewController.isPhone) { + titles = @[@"List", @"Grid"]; + values = @[@"titles_on_left", @"titles_in_grid"]; + } else { + titles = @[@"Left", @"Top", @"Bottom", @"Grid"]; + values = @[@"titles_on_left", @"titles_on_top", @"titles_on_bottom", @"titles_in_grid"]; + } + + [viewController addSegmentedControlWithTitles:titles values:values preferenceKey:preferenceKey selectionShouldDismiss:YES handler:^(NSUInteger selectedIndex) { + [self.appDelegate.detailViewController updateLayoutWithReload:YES]; + }]; + + if (self.appDelegate.detailViewController.storyTitlesInGrid) { + preferenceKey = @"grid_columns"; - NSString *preferenceKey = @"story_titles_position"; - NSArray *titles = @[@"Left", @"Top", @"Bottom", @"Grid"]; - NSArray *values = @[@"titles_on_left", @"titles_on_top", @"titles_on_bottom", @"titles_in_grid"]; + if (appDelegate.detailViewController.isPhone) { + titles = @[@"Auto Cols", @"1", @"2"]; + values = @[@"auto", @"1", @"2"]; + } else { + titles = @[@"Auto Cols", @"1", @"2", @"3", @"4"]; + values = @[@"auto", @"1", @"2", @"3", @"4"]; + } - [viewController addSegmentedControlWithTitles:titles values:values preferenceKey:preferenceKey selectionShouldDismiss:YES handler:^(NSUInteger selectedIndex) { + [viewController addSegmentedControlWithTitles:titles values:values defaultValue:@"auto" preferenceKey:preferenceKey selectionShouldDismiss:NO handler:^(NSUInteger selectedIndex) { [self.appDelegate.detailViewController updateLayoutWithReload:YES]; }]; - if (self.appDelegate.detailViewController.storyTitlesInGrid) { - NSString *preferenceKey = @"grid_columns"; - NSArray *titles = @[@"Auto Cols", @"2", @"3", @"4"]; - NSArray *values = @[@"auto", @"2", @"3", @"4"]; - - [viewController addSegmentedControlWithTitles:titles values:values defaultValue:@"auto" preferenceKey:preferenceKey selectionShouldDismiss:NO handler:^(NSUInteger selectedIndex) { - [self.appDelegate.detailViewController updateLayoutWithReload:YES]; - }]; - - preferenceKey = @"grid_height"; - titles = @[@"XS", @"Short", @"Medium", @"Tall", @"XL"]; - values = @[@"xs", @"short", @"medium", @"tall", @"xl"]; - - [viewController addSegmentedControlWithTitles:titles values:values defaultValue:@"medium" preferenceKey:preferenceKey selectionShouldDismiss:NO handler:^(NSUInteger selectedIndex) { - [self.appDelegate.detailViewController updateLayoutWithReload:YES]; - }]; - } + preferenceKey = @"grid_height"; + titles = @[@"XS", @"Short", @"Medium", @"Tall", @"XL"]; + values = @[@"xs", @"short", @"medium", @"tall", @"xl"]; + + [viewController addSegmentedControlWithTitles:titles values:values defaultValue:@"medium" preferenceKey:preferenceKey selectionShouldDismiss:NO handler:^(NSUInteger selectedIndex) { + [self.appDelegate.detailViewController updateLayoutWithReload:YES]; + }]; } if (!self.appDelegate.detailViewController.storyTitlesInGrid) { - NSString *preferenceKey = @"story_list_preview_text_size"; - NSArray *titles = @[@"Title", @"content_preview_small.png", @"content_preview_medium.png", @"content_preview_large.png"]; - NSArray *values = @[@"title", @"short", @"medium", @"long"]; + preferenceKey = @"story_list_preview_text_size"; + titles = @[@"Title", @"content_preview_small.png", @"content_preview_medium.png", @"content_preview_large.png"]; + values = @[@"title", @"short", @"medium", @"long"]; [viewController addSegmentedControlWithTitles:titles values:values preferenceKey:preferenceKey selectionShouldDismiss:NO handler:^(NSUInteger selectedIndex) { [self.appDelegate resizePreviewSize]; @@ -2447,9 +2459,9 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state }]; } - NSString *preferenceKey = @"feed_list_font_size"; - NSArray *titles = @[@"XS", @"S", @"M", @"L", @"XL"]; - NSArray *values = @[@"xs", @"small", @"medium", @"large", @"xl"]; + preferenceKey = @"feed_list_font_size"; + titles = @[@"XS", @"S", @"M", @"L", @"XL"]; + values = @[@"xs", @"small", @"medium", @"large", @"xl"]; [viewController addSegmentedControlWithTitles:titles values:values preferenceKey:preferenceKey selectionShouldDismiss:NO handler:^(NSUInteger selectedIndex) { [self.appDelegate resizeFontSize]; diff --git a/clients/ios/Classes/Story.swift b/clients/ios/Classes/Story.swift index 2eedd1e8c..35ad0e1e3 100644 --- a/clients/ios/Classes/Story.swift +++ b/clients/ios/Classes/Story.swift @@ -141,6 +141,10 @@ class StoryCache: ObservableObject { return appDelegate.detailViewController.layout == .grid } + var isPhone: Bool { + return appDelegate.detailViewController.isPhone + } + @Published var before = [Story]() @Published var selected: Story? @Published var after = [Story]() @@ -288,13 +292,13 @@ class StorySettings { // } var gridColumns: Int { - if NewsBlurAppDelegate.shared.isCompactWidth { - return 1 - } - guard let pref = UserDefaults.standard.string(forKey: "grid_columns"), let columns = Int(pref) else { //TODO: 🚧 could have extra logic to determine the ideal number of columns - return 4 + if NewsBlurAppDelegate.shared.isCompactWidth { + return 1 + } else { + return 4 + } } return columns diff --git a/clients/ios/Classes/StoryDetailObjCViewController.m b/clients/ios/Classes/StoryDetailObjCViewController.m index a8092f210..f4fb8c489 100644 --- a/clients/ios/Classes/StoryDetailObjCViewController.m +++ b/clients/ios/Classes/StoryDetailObjCViewController.m @@ -287,7 +287,7 @@ } - (void)deferredEnableScrolling { - self.webView.scrollView.scrollEnabled = !self.appDelegate.detailViewController.storyTitlesInGrid; + self.webView.scrollView.scrollEnabled = self.appDelegate.detailViewController.isPhone || !self.appDelegate.detailViewController.storyTitlesInGrid; } - (void)viewDidDisappear:(BOOL)animated { @@ -1817,7 +1817,7 @@ [self.activityIndicator stopAnimating]; - self.webView.scrollView.scrollEnabled = !self.appDelegate.detailViewController.storyTitlesInGrid; + self.webView.scrollView.scrollEnabled = self.appDelegate.detailViewController.isPhone || !self.appDelegate.detailViewController.storyTitlesInGrid; [self loadHTMLString:self.fullStoryHTML]; self.fullStoryHTML = nil; @@ -1837,7 +1837,7 @@ self.webView.hidden = NO; [self.webView setNeedsDisplay]; - if (self == self.appDelegate.storyPagesViewController.currentPage && self.appDelegate.detailViewController.storyTitlesInGrid) { + if (self == self.appDelegate.storyPagesViewController.currentPage && !self.appDelegate.detailViewController.isPhone && self.appDelegate.detailViewController.storyTitlesInGrid) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // [self.appDelegate.feedDetailViewController changedStoryHeight:self.webView.scrollView.contentSize.height]; [self.appDelegate.feedDetailViewController reload]; diff --git a/clients/ios/Resources/Settings.bundle/Root.plist b/clients/ios/Resources/Settings.bundle/Root.plist index a66021f2c..c43da3700 100644 --- a/clients/ios/Resources/Settings.bundle/Root.plist +++ b/clients/ios/Resources/Settings.bundle/Root.plist @@ -202,6 +202,26 @@ Key override_scroll_read_filter + + Type + PSMultiValueSpecifier + Title + Story titles layout + Titles + + Titles in list + Titles in grid + + DefaultValue + titles_on_left + Values + + titles_on_left + titles_in_grid + + Key + story_titles_position + Type PSMultiValueSpecifier