From e277f5a611ccc1c0cf4d2a2c76fd5e582f0c4e43 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Bertholon Date: Thu, 23 Apr 2020 19:12:14 +0200 Subject: [PATCH 01/62] Bump version number --- PlaySRG.xcodeproj/project.pbxproj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/PlaySRG.xcodeproj/project.pbxproj b/PlaySRG.xcodeproj/project.pbxproj index 048f6a479..6678255b3 100644 --- a/PlaySRG.xcodeproj/project.pbxproj +++ b/PlaySRG.xcodeproj/project.pbxproj @@ -7605,7 +7605,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 329; + CURRENT_PROJECT_VERSION = 330; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -7624,7 +7624,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MARKETING_VERSION = 3.0.1; + MARKETING_VERSION = 3.0.2; MARKETING_VERSION_SUFFIX = "-debug"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -7673,7 +7673,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 329; + CURRENT_PROJECT_VERSION = 330; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -7686,7 +7686,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MARKETING_VERSION = 3.0.1; + MARKETING_VERSION = 3.0.2; MARKETING_VERSION_SUFFIX = ""; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; @@ -8028,7 +8028,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 329; + CURRENT_PROJECT_VERSION = 330; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -8042,7 +8042,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MARKETING_VERSION = 3.0.1; + MARKETING_VERSION = 3.0.2; MARKETING_VERSION_SUFFIX = "-beta"; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; @@ -8240,7 +8240,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 329; + CURRENT_PROJECT_VERSION = 330; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -8254,7 +8254,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MARKETING_VERSION = 3.0.1; + MARKETING_VERSION = 3.0.2; MARKETING_VERSION_SUFFIX = "-nightly"; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; From be821a88dc37d99d92a8073455dd46027ca08301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Mon, 27 Apr 2020 08:34:51 +0200 Subject: [PATCH 02/62] Update FLEX --- Cartfile | 2 +- Cartfile.resolved.proprietary | 2 +- Cartfile.resolved.public | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cartfile b/Cartfile index 610cb1cdd..c38360338 100755 --- a/Cartfile +++ b/Cartfile @@ -1,5 +1,5 @@ github "defagos/CoconutKit" "3.4" -github "Flipboard/FLEX" "6d489e72c52401839386ee41aeefe1f5105c212e" +github "Flipboard/FLEX" "4.1.1" github "mapbox/Fingertips" "cdffabac5506103a2c7cc5aedeed4021df2501da" github "microsoft/appcenter-sdk-apple" ~> 3.1.0 github "SRGSSR/DZNEmptyDataSet" "v1.8.1_srg1" diff --git a/Cartfile.resolved.proprietary b/Cartfile.resolved.proprietary index 0dd947123..3f0763e50 100755 --- a/Cartfile.resolved.proprietary +++ b/Cartfile.resolved.proprietary @@ -1,4 +1,4 @@ -github "Flipboard/FLEX" "6d489e72c52401839386ee41aeefe1f5105c212e" +github "Flipboard/FLEX" "4.1.1" github "Mantle/Mantle" "2.1.0" github "SRGSSR/ComScore-iOS-watchOS-tvOS" "6.2.0" github "SRGSSR/DZNEmptyDataSet" "v1.8.1_srg1" diff --git a/Cartfile.resolved.public b/Cartfile.resolved.public index 7823b1214..b95bca851 100755 --- a/Cartfile.resolved.public +++ b/Cartfile.resolved.public @@ -1,4 +1,4 @@ -github "Flipboard/FLEX" "6d489e72c52401839386ee41aeefe1f5105c212e" +github "Flipboard/FLEX" "4.1.1" github "Mantle/Mantle" "2.1.0" github "SRGSSR/ComScore-iOS-watchOS-tvOS" "6.2.0" github "SRGSSR/DZNEmptyDataSet" "v1.8.1_srg1" From 0bcef1f71f8618c554eb1985508230378e582449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Wed, 22 Apr 2020 10:27:44 +0200 Subject: [PATCH 03/62] Fix scroll position coupling between swimlanes --- .../Sources/Home/HomeMediaListTableViewCell.m | 18 ++++++++---------- .../Sources/Home/HomeShowListTableViewCell.m | 18 ++++++++---------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/Application/Sources/Home/HomeMediaListTableViewCell.m b/Application/Sources/Home/HomeMediaListTableViewCell.m index 1fd1bb70d..86eddd1ad 100755 --- a/Application/Sources/Home/HomeMediaListTableViewCell.m +++ b/Application/Sources/Home/HomeMediaListTableViewCell.m @@ -148,16 +148,14 @@ - (void)setHomeSectionInfo:(HomeSectionInfo *)homeSectionInfo featured:(BOOL)fea self.moduleBackgroundView.backgroundColor = homeSectionInfo.module.play_backgroundColor; - dispatch_async(dispatch_get_main_queue(), ^{ - if (homeSectionInfo) { - // Restore position in rows when scrolling vertically and returning to a previously scrolled row - CGPoint maxContentOffset = self.collectionView.play_maximumContentOffset; - CGPoint contentOffset = CGPointMake(fmaxf(fminf(homeSectionInfo.contentOffset.x, maxContentOffset.x), 0.f), - homeSectionInfo.contentOffset.y); - [self.collectionView setContentOffset:contentOffset animated:NO]; - } - self.collectionView.scrollEnabled = (homeSectionInfo.items.count != 0); - }); + if (homeSectionInfo) { + // Restore position in rows when scrolling vertically and returning to a previously scrolled row + CGPoint maxContentOffset = self.collectionView.play_maximumContentOffset; + CGPoint contentOffset = CGPointMake(fmaxf(fminf(homeSectionInfo.contentOffset.x, maxContentOffset.x), 0.f), + homeSectionInfo.contentOffset.y); + [self.collectionView setContentOffset:contentOffset animated:NO]; + } + self.collectionView.scrollEnabled = (homeSectionInfo.items.count != 0); } #pragma mark UICollectionViewDataSource protocol diff --git a/Application/Sources/Home/HomeShowListTableViewCell.m b/Application/Sources/Home/HomeShowListTableViewCell.m index 641d7b7f5..2d611afe4 100755 --- a/Application/Sources/Home/HomeShowListTableViewCell.m +++ b/Application/Sources/Home/HomeShowListTableViewCell.m @@ -112,16 +112,14 @@ - (void)setHomeSectionInfo:(HomeSectionInfo *)homeSectionInfo featured:(BOOL)fea { [super setHomeSectionInfo:homeSectionInfo featured:featured]; - dispatch_async(dispatch_get_main_queue(), ^{ - if (homeSectionInfo) { - // Restore position in rows when scrolling vertically and returning to a previously scrolled row - CGPoint maxContentOffset = self.collectionView.play_maximumContentOffset; - CGPoint contentOffset = CGPointMake(fmaxf(fminf(homeSectionInfo.contentOffset.x, maxContentOffset.x), 0.f), - homeSectionInfo.contentOffset.y); - [self.collectionView setContentOffset:contentOffset animated:NO]; - } - self.collectionView.scrollEnabled = (homeSectionInfo.items.count != 0); - }); + if (homeSectionInfo) { + // Restore position in rows when scrolling vertically and returning to a previously scrolled row + CGPoint maxContentOffset = self.collectionView.play_maximumContentOffset; + CGPoint contentOffset = CGPointMake(fmaxf(fminf(homeSectionInfo.contentOffset.x, maxContentOffset.x), 0.f), + homeSectionInfo.contentOffset.y); + [self.collectionView setContentOffset:contentOffset animated:NO]; + } + self.collectionView.scrollEnabled = (homeSectionInfo.items.count != 0); } #pragma mark UICollectionViewDataSource protocol From 89c82507c916657cd4c30da506c5bab429cf6b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Mon, 27 Apr 2020 08:13:09 +0200 Subject: [PATCH 04/62] Fix preview background color --- Application/Sources/UI/Controllers/BaseViewController.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Application/Sources/UI/Controllers/BaseViewController.m b/Application/Sources/UI/Controllers/BaseViewController.m index 07a6563c3..769071315 100755 --- a/Application/Sources/UI/Controllers/BaseViewController.m +++ b/Application/Sources/UI/Controllers/BaseViewController.m @@ -359,6 +359,13 @@ - (void)contextMenuInteraction:(UIContextMenuInteraction *)interaction willPerfo }]; } +- (UITargetedPreview *)contextMenuInteraction:(UIContextMenuInteraction *)interaction previewForHighlightingMenuWithConfiguration:(UIContextMenuConfiguration *)configuration API_AVAILABLE(ios(13.0)) +{ + UIPreviewParameters *previewParameters = [[UIPreviewParameters alloc] init]; + previewParameters.backgroundColor = self.view.backgroundColor; + return [[UITargetedPreview alloc] initWithView:interaction.view parameters:previewParameters]; +} + #pragma mark UIViewControllerPreviewingDelegate protocol - (UIViewController *)previewingContext:(id)previewingContext viewControllerForLocation:(CGPoint)location From 37054d6ffdb1dfccf6b6e508686e661c66a24ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Tue, 28 Apr 2020 15:26:31 +0200 Subject: [PATCH 05/62] Avoid displaying empty screen when no swimlanes have been loaded This fixes issue #143. --- Application/Sources/Home/HomeViewController.m | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Application/Sources/Home/HomeViewController.m b/Application/Sources/Home/HomeViewController.m index 3e2ea7003..2e031acad 100755 --- a/Application/Sources/Home/HomeViewController.m +++ b/Application/Sources/Home/HomeViewController.m @@ -433,31 +433,30 @@ - (UIEdgeInsets)play_paddingContentInsets - (NSAttributedString *)titleForEmptyDataSet:(UIScrollView *)scrollView { + NSString *title = nil; + NSError *error = self.lastRequestError; if (error) { // Multiple errors. Pick the first ones if ([error hasCode:SRGNetworkErrorMultiple withinDomain:SRGNetworkErrorDomain]) { error = [error.userInfo[SRGNetworkErrorsKey] firstObject]; } - return [[NSAttributedString alloc] initWithString:error.localizedDescription - attributes:@{ NSFontAttributeName : [UIFont srg_mediumFontWithTextStyle:SRGAppearanceFontTextStyleTitle], - NSForegroundColorAttributeName : UIColor.play_lightGrayColor }]; + title = error.localizedDescription; } else { - return nil; + title = NSLocalizedString(@"No results", @"Default text displayed when no results are available"); } + + return [[NSAttributedString alloc] initWithString:title + attributes:@{ NSFontAttributeName : [UIFont srg_mediumFontWithTextStyle:SRGAppearanceFontTextStyleTitle], + NSForegroundColorAttributeName : UIColor.play_lightGrayColor }]; } - (NSAttributedString *)descriptionForEmptyDataSet:(UIScrollView *)scrollView { - if (self.lastRequestError) { - return [[NSAttributedString alloc] initWithString:NSLocalizedString(@"Pull to reload", @"Text displayed to inform the user she can pull a list to reload it") - attributes:@{ NSFontAttributeName : [UIFont srg_mediumFontWithTextStyle:SRGAppearanceFontTextStyleSubtitle], - NSForegroundColorAttributeName : UIColor.play_lightGrayColor }]; - } - else { - return nil; - } + return [[NSAttributedString alloc] initWithString:NSLocalizedString(@"Pull to reload", @"Text displayed to inform the user she can pull a list to reload it") + attributes:@{ NSFontAttributeName : [UIFont srg_mediumFontWithTextStyle:SRGAppearanceFontTextStyleSubtitle], + NSForegroundColorAttributeName : UIColor.play_lightGrayColor }]; } - (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView @@ -466,7 +465,7 @@ - (UIImage *)imageForEmptyDataSet:(UIScrollView *)scrollView return [UIImage imageNamed:@"error-90"]; } else { - return nil; + return [UIImage imageNamed:@"media-90"]; } } From b863343fd233f7029047917ed855f0270394bc8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Wed, 22 Apr 2020 12:46:30 +0200 Subject: [PATCH 06/62] Snap on items in swimlanes --- .../Sources/Home/HomeMediaListTableViewCell.m | 3 +- .../Sources/Home/HomeShowListTableViewCell.m | 3 +- .../UI/Helpers/SwimlaneCollectionViewLayout.h | 15 ++++ .../UI/Helpers/SwimlaneCollectionViewLayout.m | 83 +++++++++++++++++++ PlaySRG.xcodeproj/project.pbxproj | 16 +++- 5 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.h create mode 100644 Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m diff --git a/Application/Sources/Home/HomeMediaListTableViewCell.m b/Application/Sources/Home/HomeMediaListTableViewCell.m index 86eddd1ad..b5aa465d4 100755 --- a/Application/Sources/Home/HomeMediaListTableViewCell.m +++ b/Application/Sources/Home/HomeMediaListTableViewCell.m @@ -12,6 +12,7 @@ #import "Layout.h" #import "MediaPlayerViewController.h" #import "SRGModule+PlaySRG.h" +#import "SwimlaneCollectionViewLayout.h" #import "UICollectionView+PlaySRG.h" #import "UIColor+PlaySRG.h" #import "UIViewController+PlaySRG.h" @@ -87,7 +88,7 @@ - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStr [self.contentView addSubview:wrapperView]; self.wrapperView = wrapperView; - UICollectionViewFlowLayout *collectionViewLayout = [[UICollectionViewFlowLayout alloc] init]; + SwimlaneCollectionViewLayout *collectionViewLayout = [[SwimlaneCollectionViewLayout alloc] init]; collectionViewLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; collectionViewLayout.minimumLineSpacing = LayoutStandardMargin; collectionViewLayout.minimumInteritemSpacing = LayoutStandardMargin; diff --git a/Application/Sources/Home/HomeShowListTableViewCell.m b/Application/Sources/Home/HomeShowListTableViewCell.m index 2d611afe4..a683a3578 100755 --- a/Application/Sources/Home/HomeShowListTableViewCell.m +++ b/Application/Sources/Home/HomeShowListTableViewCell.m @@ -9,6 +9,7 @@ #import "HomeShowCollectionViewCell.h" #import "Layout.h" #import "ShowViewController.h" +#import "SwimlaneCollectionViewLayout.h" #import "UICollectionView+PlaySRG.h" #import @@ -61,7 +62,7 @@ - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStr [self.contentView addSubview:wrapperView]; self.wrapperView = wrapperView; - UICollectionViewFlowLayout *collectionViewLayout = [[UICollectionViewFlowLayout alloc] init]; + SwimlaneCollectionViewLayout *collectionViewLayout = [[SwimlaneCollectionViewLayout alloc] init]; collectionViewLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; collectionViewLayout.minimumLineSpacing = LayoutStandardMargin; collectionViewLayout.minimumInteritemSpacing = LayoutStandardMargin; diff --git a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.h b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.h new file mode 100644 index 000000000..df5da193c --- /dev/null +++ b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.h @@ -0,0 +1,15 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface SwimlaneCollectionViewLayout : UICollectionViewFlowLayout + +@end + +NS_ASSUME_NONNULL_END diff --git a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m new file mode 100644 index 000000000..cc0babcd9 --- /dev/null +++ b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m @@ -0,0 +1,83 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +#import "SwimlaneCollectionViewLayout.h" + +#import "Layout.h" + +#import + +@implementation SwimlaneCollectionViewLayout + +#pragma mark Overrides + +- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity +{ + NSAssert(self.scrollDirection == UICollectionViewScrollDirectionHorizontal, @"Swimlanes must be a horizontal layout"); + + // Extract attributes for all items which would be displayed at the proposed offset (sort them to have cells + // and supplementary views correctly ordered altogether) + CGRect proposedRect = CGRectMake(proposedContentOffset.x, + proposedContentOffset.y, + CGRectGetWidth(self.collectionView.bounds), + CGRectGetHeight(self.collectionView.bounds)); + NSArray *layoutAttributesInProposedRect = [[self layoutAttributesForElementsInRect:proposedRect] sortedArrayUsingComparator:^NSComparisonResult(UICollectionViewLayoutAttributes * _Nonnull layoutAttributes1, UICollectionViewLayoutAttributes * _Nonnull layoutAttributes2) { + CGFloat x1 = CGRectGetMinX(layoutAttributes1.frame); + CGFloat x2 = CGRectGetMinX(layoutAttributes2.frame); + + if (x1 == x2) { + return NSOrderedSame; + } + else if (x1 < x2) { + return NSOrderedAscending; + } + else { + return NSOrderedDescending; + } + }]; + + // Nothing displayed + if (layoutAttributesInProposedRect.count == 0) { + return proposedContentOffset; + } + + UICollectionViewLayoutAttributes *proposedLayoutAttributes = nil; + + // Decide on which one of the first two items we should snap if more than two items + UICollectionViewLayoutAttributes *layoutAttributes0 = layoutAttributesInProposedRect.firstObject; + if (layoutAttributesInProposedRect.count > 1) { + UICollectionViewLayoutAttributes *layoutAttributes1 = layoutAttributesInProposedRect[1]; + + // Moving to the right. Snap on the second item + if (velocity.x > 0.f) { + proposedLayoutAttributes = layoutAttributes1; + } + // Moving to the left. Snap on the first item + else if (velocity.x < 0.f) { + proposedLayoutAttributes = layoutAttributes0; + } + // Still. Snap on the first item is at least half of it is visible + else { + CGRect visibleRect0 = CGRectIntersection(layoutAttributes0.frame, proposedRect); + + if (CGRectGetWidth(visibleRect0) < 0.5f * CGRectGetWidth(layoutAttributes0.frame)) { + proposedLayoutAttributes = layoutAttributes1; + } + else { + proposedLayoutAttributes = layoutAttributes0; + } + } + } + // Snap on the only available item + else { + proposedLayoutAttributes = layoutAttributes0; + } + + // Add the margin twice to snap not only sharp, but letting the previous item be seen (if any) + return CGPointMake(CGRectGetMinX(proposedLayoutAttributes.frame) - 2 * LayoutStandardMargin, proposedContentOffset.y); +} + +@end diff --git a/PlaySRG.xcodeproj/project.pbxproj b/PlaySRG.xcodeproj/project.pbxproj index 6678255b3..aa947fc59 100644 --- a/PlaySRG.xcodeproj/project.pbxproj +++ b/PlaySRG.xcodeproj/project.pbxproj @@ -1376,6 +1376,11 @@ 6FF7BBB422A7901900FA758A /* SearchSettingsViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6FF7BBB122A7901900FA758A /* SearchSettingsViewController.storyboard */; }; 6FF7BBB522A7901900FA758A /* SearchSettingsViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6FF7BBB122A7901900FA758A /* SearchSettingsViewController.storyboard */; }; 6FF7BBB622A7901900FA758A /* SearchSettingsViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6FF7BBB122A7901900FA758A /* SearchSettingsViewController.storyboard */; }; + 6FFCB62424504C6C00D16466 /* SwimlaneCollectionViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FFCB62324504C6C00D16466 /* SwimlaneCollectionViewLayout.m */; }; + 6FFCB62524504C6C00D16466 /* SwimlaneCollectionViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FFCB62324504C6C00D16466 /* SwimlaneCollectionViewLayout.m */; }; + 6FFCB62624504C6C00D16466 /* SwimlaneCollectionViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FFCB62324504C6C00D16466 /* SwimlaneCollectionViewLayout.m */; }; + 6FFCB62724504C6C00D16466 /* SwimlaneCollectionViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FFCB62324504C6C00D16466 /* SwimlaneCollectionViewLayout.m */; }; + 6FFCB62824504C6C00D16466 /* SwimlaneCollectionViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FFCB62324504C6C00D16466 /* SwimlaneCollectionViewLayout.m */; }; 7ACE7CA000370D1208243956 /* libPods-PlaySRG-Play SRF.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D6E859054BE4FCA7A62352E /* libPods-PlaySRG-Play SRF.a */; }; E1D8F87EB9409ADB326F446F /* libPods-PlaySRG-Play RTR.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 866AF638938EFA7338C23B03 /* libPods-PlaySRG-Play RTR.a */; }; E60178521D635E130000362E /* ComScore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E60178511D635E130000362E /* ComScore.framework */; }; @@ -2230,6 +2235,8 @@ 6FF7BB9522A78DE400FA758A /* SearchSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SearchSettingsViewController.m; sourceTree = ""; }; 6FF7BB9622A78DE400FA758A /* SearchSettingSelectorCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SearchSettingSelectorCell.m; sourceTree = ""; }; 6FF7BBB122A7901900FA758A /* SearchSettingsViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SearchSettingsViewController.storyboard; sourceTree = ""; }; + 6FFCB62224504C6C00D16466 /* SwimlaneCollectionViewLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwimlaneCollectionViewLayout.h; sourceTree = ""; }; + 6FFCB62324504C6C00D16466 /* SwimlaneCollectionViewLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SwimlaneCollectionViewLayout.m; sourceTree = ""; }; 866AF638938EFA7338C23B03 /* libPods-PlaySRG-Play RTR.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PlaySRG-Play RTR.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 9BDBB65E7B9D845CB1576750 /* Pods-PlaySRG-Play RTS.nightly.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PlaySRG-Play RTS.nightly.xcconfig"; path = "Pods/Target Support Files/Pods-PlaySRG-Play RTS/Pods-PlaySRG-Play RTS.nightly.xcconfig"; sourceTree = ""; }; A0923E657497DE17CE057F6E /* Pods-PlaySRG-Play RTR.nightly.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PlaySRG-Play RTR.nightly.xcconfig"; path = "Pods/Target Support Files/Pods-PlaySRG-Play RTR/Pods-PlaySRG-Play RTR.nightly.xcconfig"; sourceTree = ""; }; @@ -3240,9 +3247,11 @@ 6F475FA01EB37BC6003021EA /* ModalTransition.m */, 6F475FA11EB37BC6003021EA /* Previewing.h */, 6F4091FF22DCF43D005F3850 /* Previewing.m */, + 6FB340D823E1A21500BC83BF /* Scrollable.h */, + 6FFCB62224504C6C00D16466 /* SwimlaneCollectionViewLayout.h */, + 6FFCB62324504C6C00D16466 /* SwimlaneCollectionViewLayout.m */, 6FF4D4DE23D5B07E008B981A /* UIVisualEffectView+PlaySRG.h */, 6FF4D4DF23D5B07E008B981A /* UIVisualEffectView+PlaySRG.m */, - 6FB340D823E1A21500BC83BF /* Scrollable.h */, ); path = Helpers; sourceTree = ""; @@ -5895,6 +5904,7 @@ 6F9D2743203AD99C00FDE899 /* Playlist.m in Sources */, 086321052258C6D000C719A6 /* WatchLaterTableViewCell.m in Sources */, E66BEC1C1DA7FCED00AD4450 /* MediaPlayerViewController.m in Sources */, + 6FFCB62424504C6C00D16466 /* SwimlaneCollectionViewLayout.m in Sources */, 6F475FC31EB37BC6003021EA /* ListRequestViewController.m in Sources */, 6F475FFF1EB37BC6003021EA /* MediaCollectionViewCell.m in Sources */, 6F12E4E222D8676600BC1718 /* SearchHeaderView.m in Sources */, @@ -6182,6 +6192,7 @@ E65FB20C1D66D69700820696 /* DailyMediasViewController.m in Sources */, 6FEC91AA21A6B39A00AA50C8 /* TableLoadMoreFooterView.m in Sources */, 6F4760831EB37DF1003021EA /* UIStackView+PlaySRG.m in Sources */, + 6FFCB62524504C6C00D16466 /* SwimlaneCollectionViewLayout.m in Sources */, 6F06E0A51DB7791300220FC6 /* ApplicationSettings.m in Sources */, 08564B1E1D41111A00381549 /* HomeSectionHeaderView.m in Sources */, 6FE686E21EB9D57400067D40 /* ChannelService.m in Sources */, @@ -6346,6 +6357,7 @@ E65FB20D1D66D69700820696 /* DailyMediasViewController.m in Sources */, 6FEC91AB21A6B39A00AA50C8 /* TableLoadMoreFooterView.m in Sources */, 6F47608A1EB37DF1003021EA /* UIStackView+PlaySRG.m in Sources */, + 6FFCB62624504C6C00D16466 /* SwimlaneCollectionViewLayout.m in Sources */, 6F06E0A61DB7791300220FC6 /* ApplicationSettings.m in Sources */, 08564B1F1D41111A00381549 /* HomeSectionHeaderView.m in Sources */, 6FE686E31EB9D57400067D40 /* ChannelService.m in Sources */, @@ -6510,6 +6522,7 @@ E65FB20E1D66D69700820696 /* DailyMediasViewController.m in Sources */, 6FEC91AC21A6B39A00AA50C8 /* TableLoadMoreFooterView.m in Sources */, 6F4760911EB37DF1003021EA /* UIStackView+PlaySRG.m in Sources */, + 6FFCB62724504C6C00D16466 /* SwimlaneCollectionViewLayout.m in Sources */, 6F06E0A71DB7791300220FC6 /* ApplicationSettings.m in Sources */, 08564B201D41111A00381549 /* HomeSectionHeaderView.m in Sources */, 6FE686E41EB9D57400067D40 /* ChannelService.m in Sources */, @@ -6674,6 +6687,7 @@ E65FB20F1D66D69700820696 /* DailyMediasViewController.m in Sources */, 6FEC91AD21A6B39A00AA50C8 /* TableLoadMoreFooterView.m in Sources */, 6F4760981EB37DF2003021EA /* UIStackView+PlaySRG.m in Sources */, + 6FFCB62824504C6C00D16466 /* SwimlaneCollectionViewLayout.m in Sources */, 6F06E0A81DB7791300220FC6 /* ApplicationSettings.m in Sources */, 08564B211D41111A00381549 /* HomeSectionHeaderView.m in Sources */, 6FE686E51EB9D57400067D40 /* ChannelService.m in Sources */, From 7091d58b376036a94c9150adfd96847c3917d6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Thu, 23 Apr 2020 09:11:34 +0200 Subject: [PATCH 07/62] Snap when restoring scroll positions --- Application/Sources/Home/HomeMediaListTableViewCell.m | 5 +++-- Application/Sources/Home/HomeShowListTableViewCell.m | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Application/Sources/Home/HomeMediaListTableViewCell.m b/Application/Sources/Home/HomeMediaListTableViewCell.m index b5aa465d4..60df4634b 100755 --- a/Application/Sources/Home/HomeMediaListTableViewCell.m +++ b/Application/Sources/Home/HomeMediaListTableViewCell.m @@ -152,8 +152,9 @@ - (void)setHomeSectionInfo:(HomeSectionInfo *)homeSectionInfo featured:(BOOL)fea if (homeSectionInfo) { // Restore position in rows when scrolling vertically and returning to a previously scrolled row CGPoint maxContentOffset = self.collectionView.play_maximumContentOffset; - CGPoint contentOffset = CGPointMake(fmaxf(fminf(homeSectionInfo.contentOffset.x, maxContentOffset.x), 0.f), - homeSectionInfo.contentOffset.y); + CGPoint proposedContentOffset = CGPointMake(fmaxf(fminf(homeSectionInfo.contentOffset.x, maxContentOffset.x), 0.f), + homeSectionInfo.contentOffset.y); + CGPoint contentOffset = [self.collectionView.collectionViewLayout targetContentOffsetForProposedContentOffset:proposedContentOffset withScrollingVelocity:CGPointZero]; [self.collectionView setContentOffset:contentOffset animated:NO]; } self.collectionView.scrollEnabled = (homeSectionInfo.items.count != 0); diff --git a/Application/Sources/Home/HomeShowListTableViewCell.m b/Application/Sources/Home/HomeShowListTableViewCell.m index a683a3578..bf9475aa7 100755 --- a/Application/Sources/Home/HomeShowListTableViewCell.m +++ b/Application/Sources/Home/HomeShowListTableViewCell.m @@ -116,8 +116,9 @@ - (void)setHomeSectionInfo:(HomeSectionInfo *)homeSectionInfo featured:(BOOL)fea if (homeSectionInfo) { // Restore position in rows when scrolling vertically and returning to a previously scrolled row CGPoint maxContentOffset = self.collectionView.play_maximumContentOffset; - CGPoint contentOffset = CGPointMake(fmaxf(fminf(homeSectionInfo.contentOffset.x, maxContentOffset.x), 0.f), - homeSectionInfo.contentOffset.y); + CGPoint proposedContentOffset = CGPointMake(fmaxf(fminf(homeSectionInfo.contentOffset.x, maxContentOffset.x), 0.f), + homeSectionInfo.contentOffset.y); + CGPoint contentOffset = [self.collectionView.collectionViewLayout targetContentOffsetForProposedContentOffset:proposedContentOffset withScrollingVelocity:CGPointZero]; [self.collectionView setContentOffset:contentOffset animated:NO]; } self.collectionView.scrollEnabled = (homeSectionInfo.items.count != 0); From 90b5665b2accb8c56aa5f8d49553b430c39adbe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Thu, 23 Apr 2020 09:19:41 +0200 Subject: [PATCH 08/62] Avoid snapping before the first item --- Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m index cc0babcd9..ac38f2afd 100644 --- a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m +++ b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m @@ -77,7 +77,7 @@ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentO } // Add the margin twice to snap not only sharp, but letting the previous item be seen (if any) - return CGPointMake(CGRectGetMinX(proposedLayoutAttributes.frame) - 2 * LayoutStandardMargin, proposedContentOffset.y); + return CGPointMake(fmaxf(CGRectGetMinX(proposedLayoutAttributes.frame) - 2 * LayoutStandardMargin, 0.f), proposedContentOffset.y); } @end From 14f9fc8e2e24514c3b6dc3921b2bc5bb2bb82287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Thu, 23 Apr 2020 09:24:28 +0200 Subject: [PATCH 09/62] Snap faster for convenient browsing --- Application/Sources/Home/HomeMediaListTableViewCell.m | 1 + Application/Sources/Home/HomeShowListTableViewCell.m | 1 + 2 files changed, 2 insertions(+) diff --git a/Application/Sources/Home/HomeMediaListTableViewCell.m b/Application/Sources/Home/HomeMediaListTableViewCell.m index 60df4634b..91fa34b53 100755 --- a/Application/Sources/Home/HomeMediaListTableViewCell.m +++ b/Application/Sources/Home/HomeMediaListTableViewCell.m @@ -99,6 +99,7 @@ - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStr collectionView.indicatorStyle = UIScrollViewIndicatorStyleWhite; collectionView.alwaysBounceHorizontal = YES; collectionView.directionalLockEnabled = YES; + collectionView.decelerationRate = UIScrollViewDecelerationRateFast; // Important. If > 1 view on-screen is found on iPhone with this property enabled, none will scroll to top collectionView.scrollsToTop = NO; collectionView.delegate = self; diff --git a/Application/Sources/Home/HomeShowListTableViewCell.m b/Application/Sources/Home/HomeShowListTableViewCell.m index bf9475aa7..742d97743 100755 --- a/Application/Sources/Home/HomeShowListTableViewCell.m +++ b/Application/Sources/Home/HomeShowListTableViewCell.m @@ -73,6 +73,7 @@ - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStr collectionView.indicatorStyle = UIScrollViewIndicatorStyleWhite; collectionView.alwaysBounceHorizontal = YES; collectionView.directionalLockEnabled = YES; + collectionView.decelerationRate = UIScrollViewDecelerationRateFast; // Important. If > 1 view on-screen is found on iPhone with this property enabled, none will scroll to top collectionView.scrollsToTop = NO; collectionView.delegate = self; From 5a825d9b5c1e639dc57000ffe083ae825a524300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Thu, 23 Apr 2020 18:09:31 +0200 Subject: [PATCH 10/62] Do not snap at the end of the content view --- .../Sources/UI/Helpers/SwimlaneCollectionViewLayout.m | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m index ac38f2afd..152156292 100644 --- a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m +++ b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m @@ -18,6 +18,11 @@ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentO { NSAssert(self.scrollDirection == UICollectionViewScrollDirectionHorizontal, @"Swimlanes must be a horizontal layout"); + // Do not snap at the end + if (proposedContentOffset.x >= self.collectionView.contentSize.width - CGRectGetWidth(self.collectionView.frame)) { + return proposedContentOffset; + } + // Extract attributes for all items which would be displayed at the proposed offset (sort them to have cells // and supplementary views correctly ordered altogether) CGRect proposedRect = CGRectMake(proposedContentOffset.x, @@ -76,8 +81,9 @@ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentO proposedLayoutAttributes = layoutAttributes0; } - // Add the margin twice to snap not only sharp, but letting the previous item be seen (if any) - return CGPointMake(fmaxf(CGRectGetMinX(proposedLayoutAttributes.frame) - 2 * LayoutStandardMargin, 0.f), proposedContentOffset.y); + // Use twice the margin to snap not only sharp, but letting the previous item be seen (if any) + CGFloat snapXOffset = fmaxf(CGRectGetMinX(proposedLayoutAttributes.frame) - 2 * LayoutStandardMargin, 0.f); + return CGPointMake(snapXOffset, proposedContentOffset.y); } @end From 4f91908cfffca5b2ad0c82a7bb796a74f6e5a8d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Thu, 23 Apr 2020 19:23:35 +0200 Subject: [PATCH 11/62] Fix incorrect scroll offset restoration --- Application/Sources/Home/HomeMediaListTableViewCell.m | 6 +----- Application/Sources/Home/HomeShowListTableViewCell.m | 6 +----- .../Sources/UI/Helpers/SwimlaneCollectionViewLayout.m | 10 ++++------ 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/Application/Sources/Home/HomeMediaListTableViewCell.m b/Application/Sources/Home/HomeMediaListTableViewCell.m index 91fa34b53..1b2bea4e8 100755 --- a/Application/Sources/Home/HomeMediaListTableViewCell.m +++ b/Application/Sources/Home/HomeMediaListTableViewCell.m @@ -13,7 +13,6 @@ #import "MediaPlayerViewController.h" #import "SRGModule+PlaySRG.h" #import "SwimlaneCollectionViewLayout.h" -#import "UICollectionView+PlaySRG.h" #import "UIColor+PlaySRG.h" #import "UIViewController+PlaySRG.h" @@ -152,10 +151,7 @@ - (void)setHomeSectionInfo:(HomeSectionInfo *)homeSectionInfo featured:(BOOL)fea if (homeSectionInfo) { // Restore position in rows when scrolling vertically and returning to a previously scrolled row - CGPoint maxContentOffset = self.collectionView.play_maximumContentOffset; - CGPoint proposedContentOffset = CGPointMake(fmaxf(fminf(homeSectionInfo.contentOffset.x, maxContentOffset.x), 0.f), - homeSectionInfo.contentOffset.y); - CGPoint contentOffset = [self.collectionView.collectionViewLayout targetContentOffsetForProposedContentOffset:proposedContentOffset withScrollingVelocity:CGPointZero]; + CGPoint contentOffset = [self.collectionView.collectionViewLayout targetContentOffsetForProposedContentOffset:homeSectionInfo.contentOffset withScrollingVelocity:CGPointZero]; [self.collectionView setContentOffset:contentOffset animated:NO]; } self.collectionView.scrollEnabled = (homeSectionInfo.items.count != 0); diff --git a/Application/Sources/Home/HomeShowListTableViewCell.m b/Application/Sources/Home/HomeShowListTableViewCell.m index 742d97743..59985c0ad 100755 --- a/Application/Sources/Home/HomeShowListTableViewCell.m +++ b/Application/Sources/Home/HomeShowListTableViewCell.m @@ -10,7 +10,6 @@ #import "Layout.h" #import "ShowViewController.h" #import "SwimlaneCollectionViewLayout.h" -#import "UICollectionView+PlaySRG.h" #import #import @@ -116,10 +115,7 @@ - (void)setHomeSectionInfo:(HomeSectionInfo *)homeSectionInfo featured:(BOOL)fea if (homeSectionInfo) { // Restore position in rows when scrolling vertically and returning to a previously scrolled row - CGPoint maxContentOffset = self.collectionView.play_maximumContentOffset; - CGPoint proposedContentOffset = CGPointMake(fmaxf(fminf(homeSectionInfo.contentOffset.x, maxContentOffset.x), 0.f), - homeSectionInfo.contentOffset.y); - CGPoint contentOffset = [self.collectionView.collectionViewLayout targetContentOffsetForProposedContentOffset:proposedContentOffset withScrollingVelocity:CGPointZero]; + CGPoint contentOffset = [self.collectionView.collectionViewLayout targetContentOffsetForProposedContentOffset:homeSectionInfo.contentOffset withScrollingVelocity:CGPointZero]; [self.collectionView setContentOffset:contentOffset animated:NO]; } self.collectionView.scrollEnabled = (homeSectionInfo.items.count != 0); diff --git a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m index 152156292..522612862 100644 --- a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m +++ b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m @@ -6,9 +6,7 @@ #import "SwimlaneCollectionViewLayout.h" -#import "Layout.h" - -#import +#import "UICollectionView+PlaySRG.h" @implementation SwimlaneCollectionViewLayout @@ -19,7 +17,7 @@ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentO NSAssert(self.scrollDirection == UICollectionViewScrollDirectionHorizontal, @"Swimlanes must be a horizontal layout"); // Do not snap at the end - if (proposedContentOffset.x >= self.collectionView.contentSize.width - CGRectGetWidth(self.collectionView.frame)) { + if (proposedContentOffset.x >= self.collectionView.play_maximumContentOffset.x) { return proposedContentOffset; } @@ -81,8 +79,8 @@ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentO proposedLayoutAttributes = layoutAttributes0; } - // Use twice the margin to snap not only sharp, but letting the previous item be seen (if any) - CGFloat snapXOffset = fmaxf(CGRectGetMinX(proposedLayoutAttributes.frame) - 2 * LayoutStandardMargin, 0.f); + // Use margin to snap not only sharp, but letting the previous item be seen (if any) + CGFloat snapXOffset = fmaxf(CGRectGetMinX(proposedLayoutAttributes.frame) - 2 * self.minimumInteritemSpacing, 0.f); return CGPointMake(snapXOffset, proposedContentOffset.y); } From 883b0f21c60c0d97232f2cb5f5f03bc0c447a4b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Thu, 23 Apr 2020 20:52:03 +0200 Subject: [PATCH 12/62] Add documentation --- .../Sources/UI/Helpers/SwimlaneCollectionViewLayout.h | 5 +++++ .../Sources/UI/Helpers/SwimlaneCollectionViewLayout.m | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.h b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.h index df5da193c..d1ae01bbd 100644 --- a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.h +++ b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.h @@ -8,6 +8,11 @@ NS_ASSUME_NONNULL_BEGIN +/** + * Standard flow layout for use in swimlanes, implementing snapping at item boundaries. + * + * @discussion Can be used in horizontal direction only. + */ @interface SwimlaneCollectionViewLayout : UICollectionViewFlowLayout @end diff --git a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m index 522612862..7b2fb3ee0 100644 --- a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m +++ b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m @@ -42,14 +42,14 @@ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentO } }]; - // Nothing displayed + // No item displayed in the rect if (layoutAttributesInProposedRect.count == 0) { return proposedContentOffset; } UICollectionViewLayoutAttributes *proposedLayoutAttributes = nil; - // Decide on which one of the first two items we should snap if more than two items + // Decide on which one of the first two items we should snap (if more than two items) UICollectionViewLayoutAttributes *layoutAttributes0 = layoutAttributesInProposedRect.firstObject; if (layoutAttributesInProposedRect.count > 1) { UICollectionViewLayoutAttributes *layoutAttributes1 = layoutAttributesInProposedRect[1]; @@ -79,7 +79,7 @@ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentO proposedLayoutAttributes = layoutAttributes0; } - // Use margin to snap not only sharp, but letting the previous item be seen (if any) + // Use twice the margin to snap not at item boundaries but a little before, so that previous items are slightly visible CGFloat snapXOffset = fmaxf(CGRectGetMinX(proposedLayoutAttributes.frame) - 2 * self.minimumInteritemSpacing, 0.f); return CGPointMake(snapXOffset, proposedContentOffset.y); } From 4a7c17ba6cfa4c2b60180eba058630623f61f5ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 24 Apr 2020 15:30:10 +0200 Subject: [PATCH 13/62] Implement all snapping methods --- Application/Sources/Home/HomeMediaListTableViewCell.m | 2 +- Application/Sources/Home/HomeShowListTableViewCell.m | 2 +- .../Sources/UI/Helpers/SwimlaneCollectionViewLayout.m | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Application/Sources/Home/HomeMediaListTableViewCell.m b/Application/Sources/Home/HomeMediaListTableViewCell.m index 1b2bea4e8..1479a32a6 100755 --- a/Application/Sources/Home/HomeMediaListTableViewCell.m +++ b/Application/Sources/Home/HomeMediaListTableViewCell.m @@ -151,7 +151,7 @@ - (void)setHomeSectionInfo:(HomeSectionInfo *)homeSectionInfo featured:(BOOL)fea if (homeSectionInfo) { // Restore position in rows when scrolling vertically and returning to a previously scrolled row - CGPoint contentOffset = [self.collectionView.collectionViewLayout targetContentOffsetForProposedContentOffset:homeSectionInfo.contentOffset withScrollingVelocity:CGPointZero]; + CGPoint contentOffset = [self.collectionView.collectionViewLayout targetContentOffsetForProposedContentOffset:homeSectionInfo.contentOffset]; [self.collectionView setContentOffset:contentOffset animated:NO]; } self.collectionView.scrollEnabled = (homeSectionInfo.items.count != 0); diff --git a/Application/Sources/Home/HomeShowListTableViewCell.m b/Application/Sources/Home/HomeShowListTableViewCell.m index 59985c0ad..c7e05e543 100755 --- a/Application/Sources/Home/HomeShowListTableViewCell.m +++ b/Application/Sources/Home/HomeShowListTableViewCell.m @@ -115,7 +115,7 @@ - (void)setHomeSectionInfo:(HomeSectionInfo *)homeSectionInfo featured:(BOOL)fea if (homeSectionInfo) { // Restore position in rows when scrolling vertically and returning to a previously scrolled row - CGPoint contentOffset = [self.collectionView.collectionViewLayout targetContentOffsetForProposedContentOffset:homeSectionInfo.contentOffset withScrollingVelocity:CGPointZero]; + CGPoint contentOffset = [self.collectionView.collectionViewLayout targetContentOffsetForProposedContentOffset:homeSectionInfo.contentOffset]; [self.collectionView setContentOffset:contentOffset animated:NO]; } self.collectionView.scrollEnabled = (homeSectionInfo.items.count != 0); diff --git a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m index 7b2fb3ee0..474fcc281 100644 --- a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m +++ b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m @@ -84,4 +84,9 @@ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentO return CGPointMake(snapXOffset, proposedContentOffset.y); } +- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset +{ + return [self targetContentOffsetForProposedContentOffset:proposedContentOffset withScrollingVelocity:CGPointZero]; +} + @end From 8e8605119267226ef28b718ea6e26be5a16344b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 24 Apr 2020 16:46:43 +0200 Subject: [PATCH 14/62] Fix typo --- Application/Sources/Home/HomeSectionInfo.m | 4 ++-- .../Player/MediaPlayerViewController.m | 2 +- .../Sources/Settings/ApplicationSettings.h | 4 ++-- .../Sources/Settings/ApplicationSettings.m | 22 +++++++++---------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Application/Sources/Home/HomeSectionInfo.m b/Application/Sources/Home/HomeSectionInfo.m index 784d76680..611783e6e 100755 --- a/Application/Sources/Home/HomeSectionInfo.m +++ b/Application/Sources/Home/HomeSectionInfo.m @@ -143,10 +143,10 @@ - (void)refreshRadioLivestreamsForVendor:(SRGVendor)vendor withRequestQueue:(SRG }; for (SRGMedia *originalMedia in originalMedias) { - NSString *selectedLiveStreamURN = ApplicationSettingSelectedLiveStreamURNForChannelUid(originalMedia.channel.uid); + NSString *selectedLivestreamURN = ApplicationSettingSelectedLivestreamURNForChannelUid(originalMedia.channel.uid); // If a regional stream has been selected by the user, replace the main channel media with it - if (selectedLiveStreamURN && ! [originalMedia.URN isEqual:selectedLiveStreamURN]) { + if (selectedLivestreamURN && ! [originalMedia.URN isEqual:selectedLivestreamURN]) { [self.pendingMedias addObject:originalMedia]; SRGRequest *request = [SRGDataProvider.currentDataProvider radioLivestreamsForVendor:vendor channelUid:originalMedia.channel.uid withCompletionBlock:^(NSArray * _Nullable channelMedias, NSHTTPURLResponse * _Nullable channelMediasHTTPResponse, NSError * _Nullable error) { diff --git a/Application/Sources/Player/MediaPlayerViewController.m b/Application/Sources/Player/MediaPlayerViewController.m index 9854b6655..dec2cb143 100755 --- a/Application/Sources/Player/MediaPlayerViewController.m +++ b/Application/Sources/Player/MediaPlayerViewController.m @@ -1726,7 +1726,7 @@ - (IBAction)selectLivestreamMedia:(id)sender [self.livestreamMedias enumerateObjectsUsingBlock:^(SRGMedia * _Nonnull media, NSUInteger idx, BOOL * _Nonnull stop) { [alertController addAction:[UIAlertAction actionWithTitle:media.title style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { - ApplicationSettingSetSelectedLiveStreamURNForChannelUid(media.channel.uid, media.URN); + ApplicationSettingSetSelectedLivestreamURNForChannelUid(media.channel.uid, media.URN); // Use the playback state if playing SRGMediaPlayerPlaybackState currentPlaybackState = self.letterboxController.playbackState; diff --git a/Application/Sources/Settings/ApplicationSettings.h b/Application/Sources/Settings/ApplicationSettings.h index df6403bea..e6a0d5158 100755 --- a/Application/Sources/Settings/ApplicationSettings.h +++ b/Application/Sources/Settings/ApplicationSettings.h @@ -72,8 +72,8 @@ OBJC_EXPORT BOOL ApplicationSettingBackgroundVideoPlaybackEnabled(void); OBJC_EXPORT BOOL ApplicationSettingSubtitleAvailabilityDisplayed(void); OBJC_EXPORT BOOL ApplicationSettingAudioDescriptionAvailabilityDisplayed(void); -OBJC_EXPORT NSString * _Nullable ApplicationSettingSelectedLiveStreamURNForChannelUid(NSString * _Nullable channelUid); -OBJC_EXPORT void ApplicationSettingSetSelectedLiveStreamURNForChannelUid(NSString * channelUid, NSString * _Nullable mediaURN); +OBJC_EXPORT NSString * _Nullable ApplicationSettingSelectedLivestreamURNForChannelUid(NSString * _Nullable channelUid); +OBJC_EXPORT void ApplicationSettingSetSelectedLivestreamURNForChannelUid(NSString * channelUid, NSString * _Nullable mediaURN); OBJC_EXPORT SRGMedia * _Nullable ApplicationSettingSelectedLivestreamMediaForChannelUid(NSString * _Nullable channelUid, NSArray * _Nullable medias); diff --git a/Application/Sources/Settings/ApplicationSettings.m b/Application/Sources/Settings/ApplicationSettings.m index 77e82f868..df4b5c26f 100755 --- a/Application/Sources/Settings/ApplicationSettings.m +++ b/Application/Sources/Settings/ApplicationSettings.m @@ -30,7 +30,7 @@ NSString * const PlaySRGSettingLastLoggedInEmailAddress = @"PlaySRGSettingLastLoggedInEmailAddress"; NSString * const PlaySRGSettingLastOpenedRadioChannelUid = @"PlaySRGSettingLastOpenedRadioChannelUid"; NSString * const PlaySRGSettingLastOpenedTabBarItem = @"PlaySRGSettingLastOpenedTabBarItem"; -NSString * const PlaySRGSettingSelectedLiveStreamURNForChannels = @"PlaySRGSettingSelectedLiveStreamURNForChannels"; +NSString * const PlaySRGSettingSelectedLivestreamURNForChannels = @"PlaySRGSettingSelectedLiveStreamURNForChannels"; NSString * const PlaySRGSettingServiceURL = @"PlaySRGSettingServiceURL"; NSString * const PlaySRGSettingUserLocation = @"PlaySRGSettingUserLocation"; @@ -207,22 +207,22 @@ BOOL ApplicationSettingAudioDescriptionAvailabilityDisplayed(void) return UIAccessibilityIsVoiceOverRunning() || [NSUserDefaults.standardUserDefaults boolForKey:PlaySRGSettingAudioDescriptionAvailabilityDisplayed]; } -NSString *ApplicationSettingSelectedLiveStreamURNForChannelUid(NSString *channelUid) +NSString *ApplicationSettingSelectedLivestreamURNForChannelUid(NSString *channelUid) { - NSDictionary *selectedLiveStreamURNForChannels = [NSUserDefaults.standardUserDefaults dictionaryForKey:PlaySRGSettingSelectedLiveStreamURNForChannels]; - return selectedLiveStreamURNForChannels[channelUid]; + NSDictionary *selectedLivestreamURNForChannels = [NSUserDefaults.standardUserDefaults dictionaryForKey:PlaySRGSettingSelectedLivestreamURNForChannels]; + return selectedLivestreamURNForChannels[channelUid]; } -void ApplicationSettingSetSelectedLiveStreamURNForChannelUid(NSString *channelUid, NSString *mediaURN) +void ApplicationSettingSetSelectedLivestreamURNForChannelUid(NSString *channelUid, NSString *mediaURN) { if (channelUid) { NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults;; - NSDictionary *selectedLiveStreamURNForChannels = [userDefaults dictionaryForKey:PlaySRGSettingSelectedLiveStreamURNForChannels]; - NSMutableDictionary *mutableSelectedLiveStreamURNForChannels = selectedLiveStreamURNForChannels.mutableCopy ?: NSMutableDictionary.new; - mutableSelectedLiveStreamURNForChannels[channelUid] = mediaURN; + NSDictionary *selectedLivestreamURNForChannels = [userDefaults dictionaryForKey:PlaySRGSettingSelectedLivestreamURNForChannels]; + NSMutableDictionary *mutableSelectedLivestreamURNForChannels = selectedLivestreamURNForChannels.mutableCopy ?: NSMutableDictionary.new; + mutableSelectedLivestreamURNForChannels[channelUid] = mediaURN; - [userDefaults setObject:mutableSelectedLiveStreamURNForChannels.copy forKey:PlaySRGSettingSelectedLiveStreamURNForChannels]; + [userDefaults setObject:mutableSelectedLivestreamURNForChannels.copy forKey:PlaySRGSettingSelectedLivestreamURNForChannels]; [userDefaults synchronize]; } } @@ -233,8 +233,8 @@ void ApplicationSettingSetSelectedLiveStreamURNForChannelUid(NSString *channelUi return nil; } - NSString *selectedLiveStreamURN = ApplicationSettingSelectedLiveStreamURNForChannelUid(channelUid); - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K == %@", @keypath(SRGMedia.new, URN), selectedLiveStreamURN]; + NSString *selectedLivestreamURN = ApplicationSettingSelectedLivestreamURNForChannelUid(channelUid); + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K == %@", @keypath(SRGMedia.new, URN), selectedLivestreamURN]; return [medias filteredArrayUsingPredicate:predicate].firstObject; } From 8740e7afbb536743a6cfd014e375c878a5044e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 24 Apr 2020 17:12:27 +0200 Subject: [PATCH 15/62] Ensure valid offsets --- .../Sources/UI/Helpers/SwimlaneCollectionViewLayout.m | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m index 474fcc281..d21550cfb 100644 --- a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m +++ b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m @@ -17,8 +17,9 @@ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentO NSAssert(self.scrollDirection == UICollectionViewScrollDirectionHorizontal, @"Swimlanes must be a horizontal layout"); // Do not snap at the end - if (proposedContentOffset.x >= self.collectionView.play_maximumContentOffset.x) { - return proposedContentOffset; + CGFloat maxX = self.collectionView.play_maximumContentOffset.x; + if (proposedContentOffset.x >= maxX) { + return CGPointMake(maxX, proposedContentOffset.y); } // Extract attributes for all items which would be displayed at the proposed offset (sort them to have cells @@ -44,7 +45,7 @@ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentO // No item displayed in the rect if (layoutAttributesInProposedRect.count == 0) { - return proposedContentOffset; + return CGPointMake(fminf(fmaxf(proposedContentOffset.x, 0.f), maxX), proposedContentOffset.y); } UICollectionViewLayoutAttributes *proposedLayoutAttributes = nil; From ec75f98358551984cfe1d01a9db2b9432732f1ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 24 Apr 2020 19:35:27 +0200 Subject: [PATCH 16/62] Use correct content size The collection content size might not have been updated yet. Use layout information --- .../Categories/UICollectionView+PlaySRG.h | 17 ----------------- .../Categories/UICollectionView+PlaySRG.m | 17 ----------------- .../UI/Helpers/SwimlaneCollectionViewLayout.m | 4 +--- PlaySRG.xcodeproj/project.pbxproj | 14 -------------- 4 files changed, 1 insertion(+), 51 deletions(-) delete mode 100755 Application/Sources/Helpers/Categories/UICollectionView+PlaySRG.h delete mode 100755 Application/Sources/Helpers/Categories/UICollectionView+PlaySRG.m diff --git a/Application/Sources/Helpers/Categories/UICollectionView+PlaySRG.h b/Application/Sources/Helpers/Categories/UICollectionView+PlaySRG.h deleted file mode 100755 index 1453f365a..000000000 --- a/Application/Sources/Helpers/Categories/UICollectionView+PlaySRG.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// Copyright (c) SRG SSR. All rights reserved. -// -// License information is available from the LICENSE file. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface UICollectionView (PlaySRG) - -@property (nonatomic, readonly) CGPoint play_maximumContentOffset; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Application/Sources/Helpers/Categories/UICollectionView+PlaySRG.m b/Application/Sources/Helpers/Categories/UICollectionView+PlaySRG.m deleted file mode 100755 index faa1bb201..000000000 --- a/Application/Sources/Helpers/Categories/UICollectionView+PlaySRG.m +++ /dev/null @@ -1,17 +0,0 @@ -// -// Copyright (c) SRG SSR. All rights reserved. -// -// License information is available from the LICENSE file. -// - -#import "UICollectionView+PlaySRG.h" - -@implementation UICollectionView (PlaySRG) - -- (CGPoint)play_maximumContentOffset -{ - return CGPointMake(fmaxf(self.contentSize.width - CGRectGetWidth(self.frame), 0.f), - fmaxf(self.contentSize.height - CGRectGetHeight(self.frame), 0.f)); -} - -@end diff --git a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m index d21550cfb..328d29441 100644 --- a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m +++ b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m @@ -6,8 +6,6 @@ #import "SwimlaneCollectionViewLayout.h" -#import "UICollectionView+PlaySRG.h" - @implementation SwimlaneCollectionViewLayout #pragma mark Overrides @@ -17,7 +15,7 @@ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentO NSAssert(self.scrollDirection == UICollectionViewScrollDirectionHorizontal, @"Swimlanes must be a horizontal layout"); // Do not snap at the end - CGFloat maxX = self.collectionView.play_maximumContentOffset.x; + CGFloat maxX = fmaxf(self.collectionViewContentSize.width - CGRectGetWidth(self.collectionView.frame), 0.f); if (proposedContentOffset.x >= maxX) { return CGPointMake(maxX, proposedContentOffset.y); } diff --git a/PlaySRG.xcodeproj/project.pbxproj b/PlaySRG.xcodeproj/project.pbxproj index aa947fc59..e42c2226b 100644 --- a/PlaySRG.xcodeproj/project.pbxproj +++ b/PlaySRG.xcodeproj/project.pbxproj @@ -879,11 +879,6 @@ 6F4760361EB37BD1003021EA /* NSDateFormatter+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47601C1EB37BD1003021EA /* NSDateFormatter+PlaySRG.m */; }; 6F4760371EB37BD1003021EA /* NSDateFormatter+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47601C1EB37BD1003021EA /* NSDateFormatter+PlaySRG.m */; }; 6F4760381EB37BD1003021EA /* NSDateFormatter+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47601C1EB37BD1003021EA /* NSDateFormatter+PlaySRG.m */; }; - 6F47603E1EB37BD1003021EA /* UICollectionView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4760201EB37BD1003021EA /* UICollectionView+PlaySRG.m */; }; - 6F47603F1EB37BD1003021EA /* UICollectionView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4760201EB37BD1003021EA /* UICollectionView+PlaySRG.m */; }; - 6F4760401EB37BD1003021EA /* UICollectionView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4760201EB37BD1003021EA /* UICollectionView+PlaySRG.m */; }; - 6F4760411EB37BD1003021EA /* UICollectionView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4760201EB37BD1003021EA /* UICollectionView+PlaySRG.m */; }; - 6F4760421EB37BD1003021EA /* UICollectionView+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F4760201EB37BD1003021EA /* UICollectionView+PlaySRG.m */; }; 6F4760781EB37D60003021EA /* UIColor+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47606B1EB37D60003021EA /* UIColor+PlaySRG.m */; }; 6F4760791EB37D60003021EA /* UIDevice+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47606D1EB37D60003021EA /* UIDevice+PlaySRG.m */; }; 6F47607A1EB37D60003021EA /* UIImage+PlaySRG.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F47606F1EB37D60003021EA /* UIImage+PlaySRG.m */; }; @@ -2032,8 +2027,6 @@ 6F47601A1EB37BD1003021EA /* PlayDurationFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlayDurationFormatter.m; sourceTree = ""; }; 6F47601B1EB37BD1003021EA /* NSDateFormatter+PlaySRG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDateFormatter+PlaySRG.h"; sourceTree = ""; }; 6F47601C1EB37BD1003021EA /* NSDateFormatter+PlaySRG.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDateFormatter+PlaySRG.m"; sourceTree = ""; }; - 6F47601F1EB37BD1003021EA /* UICollectionView+PlaySRG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+PlaySRG.h"; sourceTree = ""; }; - 6F4760201EB37BD1003021EA /* UICollectionView+PlaySRG.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+PlaySRG.m"; sourceTree = ""; }; 6F47606A1EB37D60003021EA /* UIColor+PlaySRG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+PlaySRG.h"; sourceTree = ""; }; 6F47606B1EB37D60003021EA /* UIColor+PlaySRG.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+PlaySRG.m"; sourceTree = ""; }; 6F47606C1EB37D60003021EA /* UIDevice+PlaySRG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIDevice+PlaySRG.h"; sourceTree = ""; }; @@ -3320,8 +3313,6 @@ 6F7C35B423708E8A00259BE7 /* SRGResource+PlaySRG.m */, 0852539522073D5200BCF0B1 /* UIApplication+PlaySRG.h */, 0852539622073D5200BCF0B1 /* UIApplication+PlaySRG.m */, - 6F47601F1EB37BD1003021EA /* UICollectionView+PlaySRG.h */, - 6F4760201EB37BD1003021EA /* UICollectionView+PlaySRG.m */, 6F47606A1EB37D60003021EA /* UIColor+PlaySRG.h */, 6F47606B1EB37D60003021EA /* UIColor+PlaySRG.m */, 6F47606C1EB37D60003021EA /* UIDevice+PlaySRG.h */, @@ -6020,7 +6011,6 @@ 6F475FCD1EB37BC6003021EA /* NavigationController.m in Sources */, 6FDF08FA218B126700B2AF2C /* DeprecatedFavorite.m in Sources */, 08209308208F522A00711DE4 /* PushService.m in Sources */, - 6F47603E1EB37BD1003021EA /* UICollectionView+PlaySRG.m in Sources */, 6F4760091EB37BC6003021EA /* TranslucentTitleHeaderView.m in Sources */, 08C68F8A1D38DEA100BB8AAA /* PlayAppDelegate.m in Sources */, 6F40920822DCFE2B005F3850 /* MostSearchedShowCollectionViewCell.m in Sources */, @@ -6182,7 +6172,6 @@ 6F7C35B623708E8A00259BE7 /* SRGResource+PlaySRG.m in Sources */, 081220AE1DD07B7A00BF8326 /* DownloadsViewController.m in Sources */, 087BC6641EDF1B7C00EED89B /* UILabel+PlaySRG.m in Sources */, - 6F47603F1EB37BD1003021EA /* UICollectionView+PlaySRG.m in Sources */, 082910B6239EA69C00D168F4 /* RadioChannelsViewController.m in Sources */, E694A9F61D65F02700372DF0 /* CalendarViewController.m in Sources */, 6FD88F8B22D4BC41008859EF /* UISearchBar+PlaySRG.m in Sources */, @@ -6347,7 +6336,6 @@ 6F7C35B723708E8A00259BE7 /* SRGResource+PlaySRG.m in Sources */, 081220B21DD07B7B00BF8326 /* DownloadsViewController.m in Sources */, 087BC6651EDF1B7C00EED89B /* UILabel+PlaySRG.m in Sources */, - 6F4760401EB37BD1003021EA /* UICollectionView+PlaySRG.m in Sources */, 082910B7239EA69C00D168F4 /* RadioChannelsViewController.m in Sources */, E694A9F71D65F02700372DF0 /* CalendarViewController.m in Sources */, 6FD88F8C22D4BC41008859EF /* UISearchBar+PlaySRG.m in Sources */, @@ -6512,7 +6500,6 @@ 6F7C35B823708E8A00259BE7 /* SRGResource+PlaySRG.m in Sources */, 081220B61DD07B7B00BF8326 /* DownloadsViewController.m in Sources */, 087BC6661EDF1B7C00EED89B /* UILabel+PlaySRG.m in Sources */, - 6F4760411EB37BD1003021EA /* UICollectionView+PlaySRG.m in Sources */, 082910B8239EA69C00D168F4 /* RadioChannelsViewController.m in Sources */, E694A9F81D65F02700372DF0 /* CalendarViewController.m in Sources */, 6FD88F8D22D4BC41008859EF /* UISearchBar+PlaySRG.m in Sources */, @@ -6677,7 +6664,6 @@ 6F7C35B923708E8A00259BE7 /* SRGResource+PlaySRG.m in Sources */, 081220BA1DD07B7B00BF8326 /* DownloadsViewController.m in Sources */, 087BC6671EDF1B7D00EED89B /* UILabel+PlaySRG.m in Sources */, - 6F4760421EB37BD1003021EA /* UICollectionView+PlaySRG.m in Sources */, 082910B9239EA69C00D168F4 /* RadioChannelsViewController.m in Sources */, E694A9F91D65F02700372DF0 /* CalendarViewController.m in Sources */, 6FD88F8E22D4BC41008859EF /* UISearchBar+PlaySRG.m in Sources */, From ca2b42a1a2c96bdfeb56d795cb6c846d852c11af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 1 May 2020 13:18:03 +0200 Subject: [PATCH 17/62] Deal with edge cases immediately --- .../Sources/UI/Helpers/SwimlaneCollectionViewLayout.m | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m index 328d29441..3a2823393 100644 --- a/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m +++ b/Application/Sources/UI/Helpers/SwimlaneCollectionViewLayout.m @@ -12,13 +12,16 @@ @implementation SwimlaneCollectionViewLayout - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity { - NSAssert(self.scrollDirection == UICollectionViewScrollDirectionHorizontal, @"Swimlanes must be a horizontal layout"); + NSAssert(self.scrollDirection == UICollectionViewScrollDirectionHorizontal, @"Currently only implemented for horizontal layout direction"); - // Do not snap at the end + // If already at the beginning or the end, stays there CGFloat maxX = fmaxf(self.collectionViewContentSize.width - CGRectGetWidth(self.collectionView.frame), 0.f); if (proposedContentOffset.x >= maxX) { return CGPointMake(maxX, proposedContentOffset.y); } + else if (proposedContentOffset.x <= 0.f) { + return CGPointMake(0.f, proposedContentOffset.y); + } // Extract attributes for all items which would be displayed at the proposed offset (sort them to have cells // and supplementary views correctly ordered altogether) @@ -43,7 +46,7 @@ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentO // No item displayed in the rect if (layoutAttributesInProposedRect.count == 0) { - return CGPointMake(fminf(fmaxf(proposedContentOffset.x, 0.f), maxX), proposedContentOffset.y); + return proposedContentOffset; } UICollectionViewLayoutAttributes *proposedLayoutAttributes = nil; From b24f57a288edf2f397b939f3da9d330879454fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 24 Apr 2020 22:20:09 +0200 Subject: [PATCH 18/62] Unroll tab creation --- .../Sources/UI/Controllers/TabBarController.m | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/Application/Sources/UI/Controllers/TabBarController.m b/Application/Sources/UI/Controllers/TabBarController.m index 213303aa6..d8aee9139 100755 --- a/Application/Sources/UI/Controllers/TabBarController.m +++ b/Application/Sources/UI/Controllers/TabBarController.m @@ -46,70 +46,82 @@ - (instancetype)init ApplicationConfiguration *applicationConfiguration = ApplicationConfiguration.sharedApplicationConfiguration; NSMutableArray *viewControllers = NSMutableArray.array; - NSMutableArray *tabBarItems = NSMutableArray.array; + // Videos tab ApplicationSectionInfo *videosApplicationSectionInfo = [ApplicationSectionInfo applicationSectionInfoWithApplicationSection:ApplicationSectionOverview radioChannel:nil]; UIViewController *videosViewController = [[HomeViewController alloc] initWithApplicationSectionInfo:videosApplicationSectionInfo homeSections:applicationConfiguration.videoHomeSections]; videosViewController.title = NSLocalizedString(@"Videos", @"Title displayed at the top of the video view"); - [viewControllers addObject:videosViewController]; + UITabBarItem *videosTabBarItem = [[UITabBarItem alloc] initWithTitle:videosViewController.title image:[UIImage imageNamed:@"videos-24"] tag:TabBarItemIdentifierVideos]; videosTabBarItem.accessibilityIdentifier = AccessibilityIdentifierVideosTabBarItem; - [tabBarItems addObject:videosTabBarItem]; - UIViewController *audiosViewController = nil; + NavigationController *videosNavigationController = [[NavigationController alloc] initWithRootViewController:videosViewController]; + videosNavigationController.tabBarItem = videosTabBarItem; + [viewControllers addObject:videosNavigationController]; + + // Audios tab NSArray *radioChannels = applicationConfiguration.radioChannels; if (radioChannels.count > 1) { - audiosViewController = [[RadioChannelsViewController alloc] initWithRadioChannels:radioChannels]; + UIViewController *radioChannelsViewController = [[RadioChannelsViewController alloc] initWithRadioChannels:radioChannels]; + + UITabBarItem *audiosTabBarItem = [[UITabBarItem alloc] initWithTitle:radioChannelsViewController.title image:[UIImage imageNamed:@"audios-24"] tag:TabBarItemIdentifierAudios]; + audiosTabBarItem.accessibilityIdentifier = AccessibilityIdentifierAudiosTabBarItem; + + NavigationController *audiosNavigationController = [[NavigationController alloc] initWithRootViewController:radioChannelsViewController]; + audiosNavigationController.tabBarItem = audiosTabBarItem; + [viewControllers addObject:audiosNavigationController]; } else if (radioChannels.count == 1) { RadioChannel *radioChannel = radioChannels.firstObject; ApplicationSectionInfo *audiosApplicationSectionInfo = [ApplicationSectionInfo applicationSectionInfoWithApplicationSection:ApplicationSectionOverview radioChannel:radioChannel]; - audiosViewController = [[HomeViewController alloc] initWithApplicationSectionInfo:audiosApplicationSectionInfo homeSections:radioChannel.homeSections]; + UIViewController *audiosViewController = [[HomeViewController alloc] initWithApplicationSectionInfo:audiosApplicationSectionInfo homeSections:radioChannel.homeSections]; audiosViewController.title = NSLocalizedString(@"Audios", @"Title displayed at the top of the audio view"); - } - - if (audiosViewController) { - [viewControllers addObject:audiosViewController]; + UITabBarItem *audiosTabBarItem = [[UITabBarItem alloc] initWithTitle:audiosViewController.title image:[UIImage imageNamed:@"audios-24"] tag:TabBarItemIdentifierAudios]; audiosTabBarItem.accessibilityIdentifier = AccessibilityIdentifierAudiosTabBarItem; - [tabBarItems addObject:audiosTabBarItem]; + + NavigationController *audiosNavigationController = [[NavigationController alloc] initWithRootViewController:audiosViewController]; + audiosNavigationController.tabBarItem = audiosTabBarItem; + [audiosNavigationController updateWithRadioChannel:radioChannel animated:NO]; + [viewControllers addObject:audiosNavigationController]; } + // Live tab NSArray *liveHomeSections = ApplicationConfiguration.sharedApplicationConfiguration.liveHomeSections; if (liveHomeSections.count != 0) { ApplicationSectionInfo *liveApplicationSectionInfo = [ApplicationSectionInfo applicationSectionInfoWithApplicationSection:ApplicationSectionLive radioChannel:nil]; UIViewController *liveHomeViewController = [[HomeViewController alloc] initWithApplicationSectionInfo:liveApplicationSectionInfo homeSections:liveHomeSections]; liveHomeViewController.title = NSLocalizedString(@"Livestreams", @"Title displayed at the top of the livestream view"); - [viewControllers addObject:liveHomeViewController]; + UITabBarItem *liveTabBarItem = [[UITabBarItem alloc] initWithTitle:liveHomeViewController.title image:[UIImage imageNamed:@"livestreams-24"] tag:TabBarItemIdentifierLivestreams]; liveTabBarItem.accessibilityIdentifier = AccessibilityIdentifierLivestreamsTabBarItem; - [tabBarItems addObject:liveTabBarItem]; + + NavigationController *liveNavigationController = [[NavigationController alloc] initWithRootViewController:liveHomeViewController]; + liveNavigationController.tabBarItem = liveTabBarItem; + [viewControllers addObject:liveNavigationController]; } + // Search tab UIViewController *searchViewController = [[SearchViewController alloc] init]; - [viewControllers addObject:searchViewController]; + UITabBarItem *searchTabBarItem = [[UITabBarItem alloc] initWithTitle:searchViewController.title image:[UIImage imageNamed:@"search-24"] tag:TabBarItemIdentifierSearch]; searchTabBarItem.accessibilityIdentifier = AccessibilityIdentifierSearchTabBarItem; - [tabBarItems addObject:searchTabBarItem]; + NavigationController *searchNavigationController = [[NavigationController alloc] initWithRootViewController:searchViewController]; + searchNavigationController.tabBarItem = searchTabBarItem; + [viewControllers addObject:searchNavigationController]; + + // Profile tab UIViewController *profileViewController = [[ProfileViewController alloc] init]; - [viewControllers addObject:profileViewController]; + UITabBarItem *profileTabBarItem = [[UITabBarItem alloc] initWithTitle:profileViewController.title image:[UIImage imageNamed:@"profile-24"] tag:TabBarItemIdentifierProfile]; profileTabBarItem.accessibilityIdentifier = AccessibilityIdentifierProfileTabBarItem; - [tabBarItems addObject:profileTabBarItem]; - NSMutableArray *navigationControllers = NSMutableArray.array; - [viewControllers enumerateObjectsUsingBlock:^(UIViewController * _Nonnull viewController, NSUInteger idx, BOOL * _Nonnull stop) { - NavigationController *navigationController = [[NavigationController alloc] initWithRootViewController:viewController]; - navigationController.tabBarItem = tabBarItems[idx]; - [navigationControllers addObject:navigationController]; - - if ([viewController isKindOfClass:HomeViewController.class]) { - HomeViewController *homeViewController = (HomeViewController *)viewController; - [navigationController updateWithRadioChannel:homeViewController.radioChannel animated:NO]; - } - }]; - self.viewControllers = navigationControllers.copy; + NavigationController *profileNavigationController = [[NavigationController alloc] initWithRootViewController:profileViewController]; + profileNavigationController.tabBarItem = profileTabBarItem; + [viewControllers addObject:profileNavigationController]; + + self.viewControllers = viewControllers.copy; if (@available(iOS 13, *)) { self.tabBar.barTintColor = nil; From b1cb227b9188cdbc4c74e00aa2539e1a1a67e3b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 24 Apr 2020 22:49:19 +0200 Subject: [PATCH 19/62] Display profile with a split view --- .../Sources/Library/ProfileViewController.m | 18 +++++++++++++++++- .../Sources/UI/Controllers/TabBarController.m | 8 +++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Application/Sources/Library/ProfileViewController.m b/Application/Sources/Library/ProfileViewController.m index 44d8ee8e3..c3402cbaa 100755 --- a/Application/Sources/Library/ProfileViewController.m +++ b/Application/Sources/Library/ProfileViewController.m @@ -96,6 +96,12 @@ - (void)viewDidLoad action:@selector(settings:)]; settingsBarButtonItem.accessibilityLabel = PlaySRGAccessibilityLocalizedString(@"Settings", @"Settings button label on home view"); self.navigationItem.rightBarButtonItem = settingsBarButtonItem; + + [self reloadData]; + + if (! self.splitViewController.collapsed) { + [self openApplicationSectionInfo:self.sectionInfos.firstObject animated:NO]; + } } - (void)viewWillAppear:(BOOL)animated @@ -199,7 +205,17 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI } if (viewController) { - [self.navigationController pushViewController:viewController animated:animated]; + void (^show)(void) = ^{ + // Use navigation for details. The split view takes care of moving view controllers when collapsing + NavigationController *navigationController = [[NavigationController alloc] initWithRootViewController:viewController]; + [self.splitViewController showDetailViewController:navigationController sender:@(animated)]; + }; + if (animated) { + show(); + } + else { + [UIView performWithoutAnimation:show]; + } return YES; } else { diff --git a/Application/Sources/UI/Controllers/TabBarController.m b/Application/Sources/UI/Controllers/TabBarController.m index d8aee9139..50e9cbc85 100755 --- a/Application/Sources/UI/Controllers/TabBarController.m +++ b/Application/Sources/UI/Controllers/TabBarController.m @@ -113,13 +113,15 @@ - (instancetype)init // Profile tab UIViewController *profileViewController = [[ProfileViewController alloc] init]; + NavigationController *profileNavigationController = [[NavigationController alloc] initWithRootViewController:profileViewController]; UITabBarItem *profileTabBarItem = [[UITabBarItem alloc] initWithTitle:profileViewController.title image:[UIImage imageNamed:@"profile-24"] tag:TabBarItemIdentifierProfile]; profileTabBarItem.accessibilityIdentifier = AccessibilityIdentifierProfileTabBarItem; - NavigationController *profileNavigationController = [[NavigationController alloc] initWithRootViewController:profileViewController]; - profileNavigationController.tabBarItem = profileTabBarItem; - [viewControllers addObject:profileNavigationController]; + UISplitViewController *profileSplitViewController = [[UISplitViewController alloc] init]; + profileSplitViewController.viewControllers = @[ profileNavigationController ]; + profileSplitViewController.tabBarItem = profileTabBarItem; + [viewControllers addObject:profileSplitViewController]; self.viewControllers = viewControllers.copy; From f6e285b80a2671df7b63ee4a10186e97e0233b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Sat, 25 Apr 2020 12:20:53 +0200 Subject: [PATCH 20/62] Fix incorrectly initially non-displayed primary view controller when starting in compact layout --- Application/Sources/Application/PlayAppDelegate.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Application/Sources/Application/PlayAppDelegate.m b/Application/Sources/Application/PlayAppDelegate.m index 8d0692ef6..a2af47604 100755 --- a/Application/Sources/Application/PlayAppDelegate.m +++ b/Application/Sources/Application/PlayAppDelegate.m @@ -163,8 +163,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [Download removeUnusedDownloadedFiles]; // Setup view controller hierarchy - self.window.rootViewController = [[TabBarController alloc] init]; [self.window makeKeyAndVisible]; + self.window.rootViewController = [[TabBarController alloc] init]; [self checkForForcedUpdates]; From 523100909ce1bdc55e51c039316fdc980883d80d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Sat, 25 Apr 2020 12:40:14 +0200 Subject: [PATCH 21/62] Avoid duplicate reload --- .../Sources/Library/ProfileViewController.m | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Application/Sources/Library/ProfileViewController.m b/Application/Sources/Library/ProfileViewController.m index c3402cbaa..d81de789d 100755 --- a/Application/Sources/Library/ProfileViewController.m +++ b/Application/Sources/Library/ProfileViewController.m @@ -96,12 +96,6 @@ - (void)viewDidLoad action:@selector(settings:)]; settingsBarButtonItem.accessibilityLabel = PlaySRGAccessibilityLocalizedString(@"Settings", @"Settings button label on home view"); self.navigationItem.rightBarButtonItem = settingsBarButtonItem; - - [self reloadData]; - - if (! self.splitViewController.collapsed) { - [self openApplicationSectionInfo:self.sectionInfos.firstObject animated:NO]; - } } - (void)viewWillAppear:(BOOL)animated @@ -112,6 +106,10 @@ - (void)viewWillAppear:(BOOL)animated // Ensure correct latest notifications displayed [self reloadData]; + + if ([self play_isMovingToParentViewController] && ! self.splitViewController.collapsed) { + [self openDefaultApplicationSectionAnimated:NO]; + } } - (void)viewWillDisappear:(BOOL)animated @@ -172,6 +170,10 @@ - (Notification *)notificationAtIndexPath:(NSIndexPath *)indexPath - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionInfo animated:(BOOL)animated { + if (! applicationSectionInfo) { + return NO; + } + UIViewController *viewController = nil; switch (applicationSectionInfo.applicationSection) { case ApplicationSectionNotifications: { @@ -223,6 +225,11 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI } } +- (void)openDefaultApplicationSectionAnimated:(BOOL)animated +{ + [self openApplicationSectionInfo:self.sectionInfos.firstObject animated:NO]; +} + #pragma mark ContentInsets protocol - (NSArray *)play_contentScrollViews From 5e9d163b728310f4c434f3af94500a4553a59dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Sat, 25 Apr 2020 13:15:26 +0200 Subject: [PATCH 22/62] Ensure details are correctly loaded on the fly when starting in collapsed layout --- .../Sources/Library/ProfileViewController.m | 52 +++++++++++++------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/Application/Sources/Library/ProfileViewController.m b/Application/Sources/Library/ProfileViewController.m index d81de789d..4099617dd 100755 --- a/Application/Sources/Library/ProfileViewController.m +++ b/Application/Sources/Library/ProfileViewController.m @@ -104,11 +104,11 @@ - (void)viewWillAppear:(BOOL)animated [PushService.sharedService resetApplicationBadge]; - // Ensure correct latest notifications displayed + // Ensure latest notifications are displayed [self reloadData]; - if ([self play_isMovingToParentViewController] && ! self.splitViewController.collapsed) { - [self openDefaultApplicationSectionAnimated:NO]; + if ([self play_isMovingToParentViewController]) { + [self openApplicationSectionInfo:self.sectionInfos.firstObject animated:NO]; } } @@ -168,10 +168,10 @@ - (Notification *)notificationAtIndexPath:(NSIndexPath *)indexPath return applicationSectionInfo.options[ApplicationSectionOptionNotificationKey]; } -- (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionInfo animated:(BOOL)animated +- (UIViewController *)viewControllerForSectionInfo:(ApplicationSectionInfo *)applicationSectionInfo { if (! applicationSectionInfo) { - return NO; + return nil; } UIViewController *viewController = nil; @@ -206,17 +206,40 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI } } + if (! viewController) { + return nil; + } + + // Always wrap into a navigation controller. The split view takes care of moving view controllers between navigation + // controllers when collapsing or expanding + return [[NavigationController alloc] initWithRootViewController:viewController]; +} + +- (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionInfo animated:(BOOL)animated +{ + if (! applicationSectionInfo) { + return NO; + } + + UIViewController *viewController = [self viewControllerForSectionInfo:applicationSectionInfo]; if (viewController) { - void (^show)(void) = ^{ - // Use navigation for details. The split view takes care of moving view controllers when collapsing - NavigationController *navigationController = [[NavigationController alloc] initWithRootViewController:viewController]; - [self.splitViewController showDetailViewController:navigationController sender:@(animated)]; - }; if (animated) { - show(); + [self.splitViewController showDetailViewController:viewController sender:self]; } else { - [UIView performWithoutAnimation:show]; + // Adding the details view controller on-the-fly avoids automatic collapsing (i.e. starting with the details + // on top of the primary) when starting in compact layout. + NSMutableArray *viewControllers = self.splitViewController.viewControllers.mutableCopy; + if (viewControllers.count == 1) { + [viewControllers addObject:viewController]; + } + else if (viewControllers.count == 2) { + [viewControllers replaceObjectAtIndex:1 withObject:viewController]; + } + else { + return NO; + } + self.splitViewController.viewControllers = viewControllers.copy; } return YES; } @@ -225,11 +248,6 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI } } -- (void)openDefaultApplicationSectionAnimated:(BOOL)animated -{ - [self openApplicationSectionInfo:self.sectionInfos.firstObject animated:NO]; -} - #pragma mark ContentInsets protocol - (NSArray *)play_contentScrollViews From 46f83754e6bd8f7c2bfe3a9c0a389db8741bb8e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Sat, 25 Apr 2020 13:42:42 +0200 Subject: [PATCH 23/62] Highlight current element in split layout --- .../Sources/Library/ProfileTableViewCell.m | 5 +-- .../Sources/Library/ProfileViewController.m | 31 ++++++++++++++++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/Application/Sources/Library/ProfileTableViewCell.m b/Application/Sources/Library/ProfileTableViewCell.m index 475bdeb5f..784930979 100755 --- a/Application/Sources/Library/ProfileTableViewCell.m +++ b/Application/Sources/Library/ProfileTableViewCell.m @@ -42,9 +42,6 @@ - (void)awakeFromNib [super awakeFromNib]; self.backgroundColor = UIColor.clearColor; - - // Cell highlighting is custom - self.selectionStyle = UITableViewCellSelectionStyleNone; } - (void)prepareForReuse @@ -74,7 +71,7 @@ - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated { [super setHighlighted:highlighted animated:animated]; - UIColor *color = highlighted ? UIColor.play_grayColor : UIColor.whiteColor; + UIColor *color = (highlighted && self.selectionStyle == UITableViewCellAccessoryNone) ? UIColor.play_grayColor : UIColor.whiteColor; self.titleLabel.textColor = color; self.iconImageView.tintColor = color; diff --git a/Application/Sources/Library/ProfileViewController.m b/Application/Sources/Library/ProfileViewController.m index 4099617dd..f0831352a 100755 --- a/Application/Sources/Library/ProfileViewController.m +++ b/Application/Sources/Library/ProfileViewController.m @@ -31,6 +31,7 @@ @interface ProfileViewController () @property (nonatomic) NSArray *sectionInfos; +@property (nonatomic) ApplicationSectionInfo *currentSectionInfo; @property (nonatomic, weak) IBOutlet UITableView *tableView; @@ -108,7 +109,7 @@ - (void)viewWillAppear:(BOOL)animated [self reloadData]; if ([self play_isMovingToParentViewController]) { - [self openApplicationSectionInfo:self.sectionInfos.firstObject animated:NO]; + [self openApplicationSectionInfo:self.sectionInfos.firstObject interactive:NO]; } } @@ -152,6 +153,7 @@ - (void)reloadData [self.tableView reloadData]; dispatch_async(dispatch_get_main_queue(), ^{ + [self updateSelection]; [self.tableView flashScrollIndicators]; }); } @@ -215,7 +217,7 @@ - (UIViewController *)viewControllerForSectionInfo:(ApplicationSectionInfo *)app return [[NavigationController alloc] initWithRootViewController:viewController]; } -- (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionInfo animated:(BOOL)animated +- (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionInfo interactive:(BOOL)interactive { if (! applicationSectionInfo) { return NO; @@ -223,7 +225,9 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI UIViewController *viewController = [self viewControllerForSectionInfo:applicationSectionInfo]; if (viewController) { - if (animated) { + self.currentSectionInfo = applicationSectionInfo; + + if (interactive) { [self.splitViewController showDetailViewController:viewController sender:self]; } else { @@ -240,6 +244,7 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI return NO; } self.splitViewController.viewControllers = viewControllers.copy; + [self updateSelection]; } return YES; } @@ -248,6 +253,21 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI } } +- (void)updateSelection +{ + if (! self.currentSectionInfo) { + return; + } + + NSUInteger index = [self.sectionInfos indexOfObject:self.currentSectionInfo]; + if (index == NSNotFound) { + return; + } + + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0]; + [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; +} + #pragma mark ContentInsets protocol - (NSArray *)play_contentScrollViews @@ -264,7 +284,7 @@ - (UIEdgeInsets)play_paddingContentInsets - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionInfo { - return [self openApplicationSectionInfo:applicationSectionInfo animated:NO]; + return [self openApplicationSectionInfo:applicationSectionInfo interactive:NO]; } #pragma mark Scrollable protocol @@ -330,6 +350,7 @@ - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)ce else { ProfileTableViewCell *profileTableViewCell = (ProfileTableViewCell *)cell; profileTableViewCell.applicationSectionInfo = self.sectionInfos[indexPath.row]; + profileTableViewCell.selectionStyle = self.splitViewController.collapsed ? UITableViewCellSelectionStyleNone : UITableViewCellSelectionStyleGray; } } @@ -344,7 +365,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath } else { ApplicationSectionInfo *applicationSectionInfo = self.sectionInfos[indexPath.row]; - [self openApplicationSectionInfo:applicationSectionInfo animated:YES]; + [self openApplicationSectionInfo:applicationSectionInfo interactive:YES]; } } From b245a5b359ffc52d1b134fb513261335ac3a2f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Sat, 25 Apr 2020 14:39:35 +0200 Subject: [PATCH 24/62] Prevent items from being selected again --- Application/Sources/Library/ProfileViewController.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Application/Sources/Library/ProfileViewController.m b/Application/Sources/Library/ProfileViewController.m index f0831352a..a2884b83b 100755 --- a/Application/Sources/Library/ProfileViewController.m +++ b/Application/Sources/Library/ProfileViewController.m @@ -223,6 +223,10 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI return NO; } + if (! self.splitViewController.collapsed && [applicationSectionInfo isEqual:self.currentSectionInfo]) { + return YES; + } + UIViewController *viewController = [self viewControllerForSectionInfo:applicationSectionInfo]; if (viewController) { self.currentSectionInfo = applicationSectionInfo; From 1b84945e95e614243a8af84409cd341507a2c911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Sat, 25 Apr 2020 14:56:36 +0200 Subject: [PATCH 25/62] Avoid starting deeper in the navigation on iPhone --- Application/Sources/Library/ProfileViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Application/Sources/Library/ProfileViewController.m b/Application/Sources/Library/ProfileViewController.m index a2884b83b..cb8e90a23 100755 --- a/Application/Sources/Library/ProfileViewController.m +++ b/Application/Sources/Library/ProfileViewController.m @@ -108,7 +108,7 @@ - (void)viewWillAppear:(BOOL)animated // Ensure latest notifications are displayed [self reloadData]; - if ([self play_isMovingToParentViewController]) { + if ([self play_isMovingToParentViewController] && UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) { [self openApplicationSectionInfo:self.sectionInfos.firstObject interactive:NO]; } } From 1f7a50df804fba9a7fbc051dd990387263f4be17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Sat, 25 Apr 2020 14:56:46 +0200 Subject: [PATCH 26/62] Fix status bar --- .../UI/Controllers/SplitViewController.h | 15 ++++++++++ .../UI/Controllers/SplitViewController.m | 28 +++++++++++++++++++ .../Sources/UI/Controllers/TabBarController.m | 3 +- PlaySRG.xcodeproj/project.pbxproj | 14 ++++++++++ 4 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 Application/Sources/UI/Controllers/SplitViewController.h create mode 100644 Application/Sources/UI/Controllers/SplitViewController.m diff --git a/Application/Sources/UI/Controllers/SplitViewController.h b/Application/Sources/UI/Controllers/SplitViewController.h new file mode 100644 index 000000000..4d3714ade --- /dev/null +++ b/Application/Sources/UI/Controllers/SplitViewController.h @@ -0,0 +1,15 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface SplitViewController : UISplitViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Application/Sources/UI/Controllers/SplitViewController.m b/Application/Sources/UI/Controllers/SplitViewController.m new file mode 100644 index 000000000..9997e310a --- /dev/null +++ b/Application/Sources/UI/Controllers/SplitViewController.m @@ -0,0 +1,28 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +#import "SplitViewController.h" + +@implementation SplitViewController + +#pragma mark Status bar + +- (BOOL)prefersStatusBarHidden +{ + return [self.viewControllers.firstObject prefersStatusBarHidden]; +} + +- (UIStatusBarStyle)preferredStatusBarStyle +{ + return [self.viewControllers.firstObject preferredStatusBarStyle]; +} + +- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation +{ + return [self.viewControllers.firstObject preferredStatusBarUpdateAnimation]; +} + +@end diff --git a/Application/Sources/UI/Controllers/TabBarController.m b/Application/Sources/UI/Controllers/TabBarController.m index 50e9cbc85..a78d9456d 100755 --- a/Application/Sources/UI/Controllers/TabBarController.m +++ b/Application/Sources/UI/Controllers/TabBarController.m @@ -17,6 +17,7 @@ #import "RadioChannelsViewController.h" #import "Scrollable.h" #import "SearchViewController.h" +#import "SplitViewController.h" #import "UIColor+PlaySRG.h" #import @@ -118,7 +119,7 @@ - (instancetype)init UITabBarItem *profileTabBarItem = [[UITabBarItem alloc] initWithTitle:profileViewController.title image:[UIImage imageNamed:@"profile-24"] tag:TabBarItemIdentifierProfile]; profileTabBarItem.accessibilityIdentifier = AccessibilityIdentifierProfileTabBarItem; - UISplitViewController *profileSplitViewController = [[UISplitViewController alloc] init]; + SplitViewController *profileSplitViewController = [[SplitViewController alloc] init]; profileSplitViewController.viewControllers = @[ profileNavigationController ]; profileSplitViewController.tabBarItem = profileTabBarItem; [viewControllers addObject:profileSplitViewController]; diff --git a/PlaySRG.xcodeproj/project.pbxproj b/PlaySRG.xcodeproj/project.pbxproj index e42c2226b..2deeef918 100644 --- a/PlaySRG.xcodeproj/project.pbxproj +++ b/PlaySRG.xcodeproj/project.pbxproj @@ -475,6 +475,11 @@ 6F00875F219AA888004BD6FF /* StoreReview.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F00875C219AA888004BD6FF /* StoreReview.m */; }; 6F008760219AA888004BD6FF /* StoreReview.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F00875C219AA888004BD6FF /* StoreReview.m */; }; 6F008761219AA888004BD6FF /* StoreReview.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F00875C219AA888004BD6FF /* StoreReview.m */; }; + 6F0506E7245468EE0053253E /* SplitViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0506E6245468EE0053253E /* SplitViewController.m */; }; + 6F0506E8245468EE0053253E /* SplitViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0506E6245468EE0053253E /* SplitViewController.m */; }; + 6F0506E9245468EE0053253E /* SplitViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0506E6245468EE0053253E /* SplitViewController.m */; }; + 6F0506EA245468EE0053253E /* SplitViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0506E6245468EE0053253E /* SplitViewController.m */; }; + 6F0506EB245468EE0053253E /* SplitViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0506E6245468EE0053253E /* SplitViewController.m */; }; 6F06E0A41DB7791300220FC6 /* ApplicationSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F06E0A31DB7791300220FC6 /* ApplicationSettings.m */; }; 6F06E0A51DB7791300220FC6 /* ApplicationSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F06E0A31DB7791300220FC6 /* ApplicationSettings.m */; }; 6F06E0A61DB7791300220FC6 /* ApplicationSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F06E0A31DB7791300220FC6 /* ApplicationSettings.m */; }; @@ -1880,6 +1885,8 @@ 5CA73886C1B816E3660CB5E6 /* Pods-PlaySRG-Play SRF.nightly.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PlaySRG-Play SRF.nightly.xcconfig"; path = "Pods/Target Support Files/Pods-PlaySRG-Play SRF/Pods-PlaySRG-Play SRF.nightly.xcconfig"; sourceTree = ""; }; 6F00875B219AA888004BD6FF /* StoreReview.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StoreReview.h; sourceTree = ""; }; 6F00875C219AA888004BD6FF /* StoreReview.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StoreReview.m; sourceTree = ""; }; + 6F0506E5245468EE0053253E /* SplitViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SplitViewController.h; sourceTree = ""; }; + 6F0506E6245468EE0053253E /* SplitViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SplitViewController.m; sourceTree = ""; }; 6F05F254203C2F0200EEA894 /* UIViewController+PlaySRG_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+PlaySRG_Private.h"; sourceTree = ""; }; 6F06E0A21DB7791300220FC6 /* ApplicationSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ApplicationSettings.h; sourceTree = ""; }; 6F06E0A31DB7791300220FC6 /* ApplicationSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ApplicationSettings.m; sourceTree = ""; }; @@ -3225,6 +3232,8 @@ 6F475F9A1EB37BC6003021EA /* PageViewController.m */, 6F475F9C1EB37BC6003021EA /* RequestViewController.h */, 6F475F9D1EB37BC6003021EA /* RequestViewController.m */, + 6F0506E5245468EE0053253E /* SplitViewController.h */, + 6F0506E6245468EE0053253E /* SplitViewController.m */, 082910AC239E90D200D168F4 /* TabBarController.h */, 082910AD239E90D200D168F4 /* TabBarController.m */, 6F80E9C021A682E60027CA2F /* TableRequestViewController.h */, @@ -5908,6 +5917,7 @@ 08AA55BB1D49EFBD00C5026E /* HomeTableViewCell.m in Sources */, 0806E7B81D50D918002ED406 /* SettingsViewController.m in Sources */, 081220C11DD0ADAC00BF8326 /* DownloadSession.m in Sources */, + 6F0506E7245468EE0053253E /* SplitViewController.m in Sources */, E6E2DF1A1D6ACA8B00791EDE /* ShowViewController.m in Sources */, 6F475FDC1EB37BC6003021EA /* RequestViewController.m in Sources */, 6F475FF51EB37BC6003021EA /* CollectionLoadMoreFooterView.m in Sources */, @@ -6191,6 +6201,7 @@ 08C68F8B1D38DEA100BB8AAA /* PlayAppDelegate.m in Sources */, 08B77AF1240A86FD00A3BC3B /* AccessibilityIdentifierConstants.m in Sources */, 0875851423A0025F00FA7207 /* ProfileAccountHeaderView.m in Sources */, + 6F0506E8245468EE0053253E /* SplitViewController.m in Sources */, 6F4760821EB37DF1003021EA /* UIImageView+PlaySRG.m in Sources */, 087584F023A0008500FA7207 /* ApplicationSectionInfo.m in Sources */, 6F4760301EB37BD1003021EA /* PlayDurationFormatter.m in Sources */, @@ -6355,6 +6366,7 @@ 08C68F8C1D38DEA100BB8AAA /* PlayAppDelegate.m in Sources */, 08B77AF2240A86FD00A3BC3B /* AccessibilityIdentifierConstants.m in Sources */, 0875851523A0025F00FA7207 /* ProfileAccountHeaderView.m in Sources */, + 6F0506E9245468EE0053253E /* SplitViewController.m in Sources */, 6F4760891EB37DF1003021EA /* UIImageView+PlaySRG.m in Sources */, 087584F123A0008500FA7207 /* ApplicationSectionInfo.m in Sources */, 6F4760311EB37BD1003021EA /* PlayDurationFormatter.m in Sources */, @@ -6519,6 +6531,7 @@ 08C68F8D1D38DEA100BB8AAA /* PlayAppDelegate.m in Sources */, 08B77AF3240A86FD00A3BC3B /* AccessibilityIdentifierConstants.m in Sources */, 0875851623A0025F00FA7207 /* ProfileAccountHeaderView.m in Sources */, + 6F0506EA245468EE0053253E /* SplitViewController.m in Sources */, 6F4760901EB37DF1003021EA /* UIImageView+PlaySRG.m in Sources */, 087584F223A0008500FA7207 /* ApplicationSectionInfo.m in Sources */, 6F4760321EB37BD1003021EA /* PlayDurationFormatter.m in Sources */, @@ -6683,6 +6696,7 @@ 08C68F8E1D38DEA100BB8AAA /* PlayAppDelegate.m in Sources */, 08B77AF4240A86FD00A3BC3B /* AccessibilityIdentifierConstants.m in Sources */, 0875851723A0025F00FA7207 /* ProfileAccountHeaderView.m in Sources */, + 6F0506EB245468EE0053253E /* SplitViewController.m in Sources */, 6F4760971EB37DF2003021EA /* UIImageView+PlaySRG.m in Sources */, 087584F323A0008500FA7207 /* ApplicationSectionInfo.m in Sources */, 6F4760331EB37BD1003021EA /* PlayDurationFormatter.m in Sources */, From 65830a1b31a606ab10fe9ef614f1fd7ab99ba624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Sat, 25 Apr 2020 15:10:49 +0200 Subject: [PATCH 27/62] Fix 3D shortcuts --- .../Sources/Library/ProfileViewController.m | 2 +- .../Sources/UI/Controllers/SplitViewController.h | 4 +++- .../Sources/UI/Controllers/SplitViewController.m | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Application/Sources/Library/ProfileViewController.m b/Application/Sources/Library/ProfileViewController.m index cb8e90a23..aaa33ac0a 100755 --- a/Application/Sources/Library/ProfileViewController.m +++ b/Application/Sources/Library/ProfileViewController.m @@ -288,7 +288,7 @@ - (UIEdgeInsets)play_paddingContentInsets - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionInfo { - return [self openApplicationSectionInfo:applicationSectionInfo interactive:NO]; + return [self openApplicationSectionInfo:applicationSectionInfo interactive:YES]; } #pragma mark Scrollable protocol diff --git a/Application/Sources/UI/Controllers/SplitViewController.h b/Application/Sources/UI/Controllers/SplitViewController.h index 4d3714ade..936072e79 100644 --- a/Application/Sources/UI/Controllers/SplitViewController.h +++ b/Application/Sources/UI/Controllers/SplitViewController.h @@ -4,11 +4,13 @@ // License information is available from the LICENSE file. // +#import "PlayApplicationNavigation.h" + #import NS_ASSUME_NONNULL_BEGIN -@interface SplitViewController : UISplitViewController +@interface SplitViewController : UISplitViewController @end diff --git a/Application/Sources/UI/Controllers/SplitViewController.m b/Application/Sources/UI/Controllers/SplitViewController.m index 9997e310a..65afc2af8 100644 --- a/Application/Sources/UI/Controllers/SplitViewController.m +++ b/Application/Sources/UI/Controllers/SplitViewController.m @@ -25,4 +25,18 @@ - (UIStatusBarAnimation)preferredStatusBarUpdateAnimation return [self.viewControllers.firstObject preferredStatusBarUpdateAnimation]; } +#pragma mark PlayApplicationNavigation protocol + +- (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionInfo +{ + UIViewController *primaryViewController = self.viewControllers.firstObject; + if ([primaryViewController conformsToProtocol:@protocol(PlayApplicationNavigation)]) { + UIViewController *navigablePrimaryViewController = (UIViewController *)primaryViewController; + return [navigablePrimaryViewController openApplicationSectionInfo:applicationSectionInfo]; + } + else { + return NO; + } +} + @end From e06df5caa59ad7ab0d93b2b61bad1508beda77be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Sat, 25 Apr 2020 15:18:41 +0200 Subject: [PATCH 28/62] Force both panels to be visible in regular layouts --- Application/Sources/UI/Controllers/TabBarController.m | 1 + 1 file changed, 1 insertion(+) diff --git a/Application/Sources/UI/Controllers/TabBarController.m b/Application/Sources/UI/Controllers/TabBarController.m index a78d9456d..c1d48024c 100755 --- a/Application/Sources/UI/Controllers/TabBarController.m +++ b/Application/Sources/UI/Controllers/TabBarController.m @@ -120,6 +120,7 @@ - (instancetype)init profileTabBarItem.accessibilityIdentifier = AccessibilityIdentifierProfileTabBarItem; SplitViewController *profileSplitViewController = [[SplitViewController alloc] init]; + profileSplitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible; profileSplitViewController.viewControllers = @[ profileNavigationController ]; profileSplitViewController.tabBarItem = profileTabBarItem; [viewControllers addObject:profileSplitViewController]; From 7b98d3a66795c1d106f217a7ad128b39c407881d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Sat, 25 Apr 2020 15:27:29 +0200 Subject: [PATCH 29/62] Avoid animations when opening a view with 3D shortcuts --- .../Sources/Library/ProfileViewController.m | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Application/Sources/Library/ProfileViewController.m b/Application/Sources/Library/ProfileViewController.m index aaa33ac0a..0744709a8 100755 --- a/Application/Sources/Library/ProfileViewController.m +++ b/Application/Sources/Library/ProfileViewController.m @@ -109,7 +109,7 @@ - (void)viewWillAppear:(BOOL)animated [self reloadData]; if ([self play_isMovingToParentViewController] && UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) { - [self openApplicationSectionInfo:self.sectionInfos.firstObject interactive:NO]; + [self openApplicationSectionInfo:self.sectionInfos.firstObject interactive:NO animated:NO]; } } @@ -217,7 +217,7 @@ - (UIViewController *)viewControllerForSectionInfo:(ApplicationSectionInfo *)app return [[NavigationController alloc] initWithRootViewController:viewController]; } -- (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionInfo interactive:(BOOL)interactive +- (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionInfo interactive:(BOOL)interactive animated:(BOOL)animated { if (! applicationSectionInfo) { return NO; @@ -232,7 +232,16 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI self.currentSectionInfo = applicationSectionInfo; if (interactive) { - [self.splitViewController showDetailViewController:viewController sender:self]; + void (^showDetail)(void) = ^{ + [self.splitViewController showDetailViewController:viewController sender:self]; + }; + + if (animated) { + showDetail(); + } + else { + [UIView performWithoutAnimation:showDetail]; + } } else { // Adding the details view controller on-the-fly avoids automatic collapsing (i.e. starting with the details @@ -288,7 +297,7 @@ - (UIEdgeInsets)play_paddingContentInsets - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionInfo { - return [self openApplicationSectionInfo:applicationSectionInfo interactive:YES]; + return [self openApplicationSectionInfo:applicationSectionInfo interactive:YES animated:NO]; } #pragma mark Scrollable protocol @@ -369,7 +378,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath } else { ApplicationSectionInfo *applicationSectionInfo = self.sectionInfos[indexPath.row]; - [self openApplicationSectionInfo:applicationSectionInfo interactive:YES]; + [self openApplicationSectionInfo:applicationSectionInfo interactive:YES animated:YES]; } } From 358e98c327b1103daf3455f88d8825e18afde496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Sat, 25 Apr 2020 19:01:21 +0200 Subject: [PATCH 30/62] Properly respond to the user tapping the currently active tab, even with split view controller containment --- Application/Sources/Home/HomeViewController.h | 4 ++-- Application/Sources/Home/HomeViewController.m | 14 +++++------ .../Sources/Library/ProfileViewController.h | 4 ++-- .../Sources/Library/ProfileViewController.m | 14 +++++------ .../CollectionRequestViewController.h | 4 ++-- .../CollectionRequestViewController.m | 4 ++-- .../UI/Controllers/NavigationController.h | 4 ++-- .../UI/Controllers/NavigationController.m | 15 ++++++++---- .../UI/Controllers/PageViewController.h | 4 ++-- .../UI/Controllers/PageViewController.m | 20 ++++++++-------- .../UI/Controllers/SplitViewController.h | 3 ++- .../UI/Controllers/SplitViewController.m | 21 +++++++++++++++++ .../Sources/UI/Controllers/TabBarController.m | 8 +++---- .../Controllers/TableRequestViewController.h | 4 ++-- .../Controllers/TableRequestViewController.m | 4 ++-- Application/Sources/UI/Helpers/Scrollable.h | 20 ---------------- .../Sources/UI/Helpers/TabBarActionable.h | 23 +++++++++++++++++++ PlaySRG.xcodeproj/project.pbxproj | 3 ++- 18 files changed, 102 insertions(+), 71 deletions(-) delete mode 100644 Application/Sources/UI/Helpers/Scrollable.h create mode 100644 Application/Sources/UI/Helpers/TabBarActionable.h diff --git a/Application/Sources/Home/HomeViewController.h b/Application/Sources/Home/HomeViewController.h index b38c2afba..a1c2e6442 100755 --- a/Application/Sources/Home/HomeViewController.h +++ b/Application/Sources/Home/HomeViewController.h @@ -9,14 +9,14 @@ #import "PlayApplicationNavigation.h" #import "RequestViewController.h" #import "RadioChannel.h" -#import "Scrollable.h" +#import "TabBarActionable.h" #import #import NS_ASSUME_NONNULL_BEGIN -@interface HomeViewController : RequestViewController +@interface HomeViewController : RequestViewController /** * Instantiate a home for the specified application section, displayed the provided home sections. diff --git a/Application/Sources/Home/HomeViewController.m b/Application/Sources/Home/HomeViewController.m index 2e031acad..8be067640 100755 --- a/Application/Sources/Home/HomeViewController.m +++ b/Application/Sources/Home/HomeViewController.m @@ -510,13 +510,6 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI } } -#pragma mark Scrollable protocol - -- (void)scrollToTopAnimated:(BOOL)animated -{ - [self.tableView play_scrollToTopAnimated:animated]; -} - #pragma mark SRGAnalyticsViewTracking protocol - (NSString *)srg_pageViewTitle @@ -537,6 +530,13 @@ - (NSString *)srg_pageViewTitle } } +#pragma mark TabBarActionable protocol + +- (void)performActiveTabActionAnimated:(BOOL)animated +{ + [self.tableView play_scrollToTopAnimated:animated]; +} + #pragma mark UITableViewDataSource protocol - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView diff --git a/Application/Sources/Library/ProfileViewController.h b/Application/Sources/Library/ProfileViewController.h index e4b08ec37..7452d0c68 100755 --- a/Application/Sources/Library/ProfileViewController.h +++ b/Application/Sources/Library/ProfileViewController.h @@ -7,13 +7,13 @@ #import "BaseViewController.h" #import "ContentInsets.h" #import "PlayApplicationNavigation.h" -#import "Scrollable.h" +#import "TabBarActionable.h" #import NS_ASSUME_NONNULL_BEGIN -@interface ProfileViewController : BaseViewController +@interface ProfileViewController : BaseViewController @end diff --git a/Application/Sources/Library/ProfileViewController.m b/Application/Sources/Library/ProfileViewController.m index 0744709a8..56a9dd1cb 100755 --- a/Application/Sources/Library/ProfileViewController.m +++ b/Application/Sources/Library/ProfileViewController.m @@ -300,13 +300,6 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI return [self openApplicationSectionInfo:applicationSectionInfo interactive:YES animated:NO]; } -#pragma mark Scrollable protocol - -- (void)scrollToTopAnimated:(BOOL)animated -{ - [self.tableView play_scrollToTopAnimated:animated]; -} - #pragma mark SRGAnalyticsViewTracking protocol - (NSString *)srg_pageViewTitle @@ -319,6 +312,13 @@ - (NSString *)srg_pageViewTitle return @[ AnalyticsPageLevelPlay, AnalyticsPageLevelUser ]; } +#pragma mark TabBarActionable protocol + +- (void)performActiveTabActionAnimated:(BOOL)animated +{ + [self.tableView play_scrollToTopAnimated:animated]; +} + #pragma mark UITableViewDataSource protocol - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView diff --git a/Application/Sources/UI/Controllers/CollectionRequestViewController.h b/Application/Sources/UI/Controllers/CollectionRequestViewController.h index eb3623751..7f5d5fd04 100755 --- a/Application/Sources/UI/Controllers/CollectionRequestViewController.h +++ b/Application/Sources/UI/Controllers/CollectionRequestViewController.h @@ -6,7 +6,7 @@ #import "ContentInsets.h" #import "ListRequestViewController.h" -#import "Scrollable.h" +#import "TabBarActionable.h" #import @@ -21,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN * - Automatic display of a load more footer if more data is available * - Clean display of empty collections, whether because of an error or because no items are available */ -@interface CollectionRequestViewController : ListRequestViewController +@interface CollectionRequestViewController : ListRequestViewController /** * The collection view. A flow layout is required diff --git a/Application/Sources/UI/Controllers/CollectionRequestViewController.m b/Application/Sources/UI/Controllers/CollectionRequestViewController.m index 6c4c6b599..d210d3d34 100755 --- a/Application/Sources/UI/Controllers/CollectionRequestViewController.m +++ b/Application/Sources/UI/Controllers/CollectionRequestViewController.m @@ -259,9 +259,9 @@ - (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView *)scrollView return VerticalOffsetForEmptyDataSet(scrollView); } -#pragma mark Scrollable protocol +#pragma mark TabBarActionable protocol -- (void)scrollToTopAnimated:(BOOL)animated +- (void)performActiveTabActionAnimated:(BOOL)animated { [self.collectionView play_scrollToTopAnimated:animated]; } diff --git a/Application/Sources/UI/Controllers/NavigationController.h b/Application/Sources/UI/Controllers/NavigationController.h index f986bf238..0ec1df8ec 100755 --- a/Application/Sources/UI/Controllers/NavigationController.h +++ b/Application/Sources/UI/Controllers/NavigationController.h @@ -7,7 +7,7 @@ #import "RadioChannel.h" #import "PlayApplicationNavigation.h" -#import "Scrollable.h" +#import "TabBarActionable.h" #import @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN /** * Standard navigation controller with Play look-and-feel and behavior. */ -@interface NavigationController : UINavigationController +@interface NavigationController : UINavigationController /** * Create a navigation controller with standard customizable look-and-feel. diff --git a/Application/Sources/UI/Controllers/NavigationController.m b/Application/Sources/UI/Controllers/NavigationController.m index 427a7a9f1..8dbaf8ea1 100755 --- a/Application/Sources/UI/Controllers/NavigationController.m +++ b/Application/Sources/UI/Controllers/NavigationController.m @@ -175,17 +175,22 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI } } -#pragma mark Scrollable protocol +#pragma mark TabBarActionable protocol -- (void)scrollToTopAnimated:(BOOL)animated +- (void)performActiveTabActionAnimated:(BOOL)animated { if (self.viewControllers.count == 1) { UIViewController *rootViewController = self.viewControllers.firstObject; - if ([rootViewController conformsToProtocol:@protocol(Scrollable)]) { - UIViewController *scrollableRootViewController = (UIViewController *)rootViewController; - [scrollableRootViewController scrollToTopAnimated:animated]; + if ([rootViewController conformsToProtocol:@protocol(TabBarActionable)]) { + UIViewController *actionableRootViewController = (UIViewController *)rootViewController; + [actionableRootViewController performActiveTabActionAnimated:animated]; } } + else { + // Natively performed when a navigation controller is directly embedd in a tab bar controller, but here triggered + // explicitly for all other kinds of embedding as well (e.g. tab bar -> split view -> navigation). + [self popToRootViewControllerAnimated:animated]; + } } @end diff --git a/Application/Sources/UI/Controllers/PageViewController.h b/Application/Sources/UI/Controllers/PageViewController.h index df5fc95a2..9d054f828 100755 --- a/Application/Sources/UI/Controllers/PageViewController.h +++ b/Application/Sources/UI/Controllers/PageViewController.h @@ -5,7 +5,7 @@ // #import "ContentInsets.h" -#import "Scrollable.h" +#import "TabBarActionable.h" #import #import @@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN * * To use `PageViewController`, bind its `placeholderViews` property to a single view where pages will be displayed. */ -@interface PageViewController : HLSPlaceholderViewController +@interface PageViewController : HLSPlaceholderViewController /** * Create an instance displaying the supplied view controllers, and starting at the specified page. diff --git a/Application/Sources/UI/Controllers/PageViewController.m b/Application/Sources/UI/Controllers/PageViewController.m index c591cbe57..fcfe79545 100755 --- a/Application/Sources/UI/Controllers/PageViewController.m +++ b/Application/Sources/UI/Controllers/PageViewController.m @@ -237,22 +237,22 @@ - (void)tabBar:(MDCTabBar *)tabBar didSelectItem:(UITabBarItem *)item [self displayPageAtIndex:index animated:YES]; } -#pragma mark Scrollable protocol +#pragma mark SRGAnalyticsContainerViewTracking protocol -- (void)scrollToTopAnimated:(BOOL)animated +- (NSArray *)srg_activeChildViewControllers { - UIViewController *currentViewController = self.pageViewController.viewControllers.firstObject; - if ([currentViewController conformsToProtocol:@protocol(Scrollable)]) { - UIViewController *scrollableCurrentViewController = (UIViewController *)currentViewController; - [scrollableCurrentViewController scrollToTopAnimated:animated]; - } + return self.pageViewController ? @[self.pageViewController] : @[]; } -#pragma mark SRGAnalyticsContainerViewTracking protocol +#pragma mark Scrollable protocol -- (NSArray *)srg_activeChildViewControllers +- (void)performActiveTabActionAnimated:(BOOL)animated { - return self.pageViewController ? @[self.pageViewController] : @[]; + UIViewController *currentViewController = self.pageViewController.viewControllers.firstObject; + if ([currentViewController conformsToProtocol:@protocol(TabBarActionable)]) { + UIViewController *actionableCurrentViewController = (UIViewController *)currentViewController; + [actionableCurrentViewController performActiveTabActionAnimated:animated]; + } } #pragma mark UIPageViewControllerDataSource protocol diff --git a/Application/Sources/UI/Controllers/SplitViewController.h b/Application/Sources/UI/Controllers/SplitViewController.h index 936072e79..22a573bee 100644 --- a/Application/Sources/UI/Controllers/SplitViewController.h +++ b/Application/Sources/UI/Controllers/SplitViewController.h @@ -5,12 +5,13 @@ // #import "PlayApplicationNavigation.h" +#import "TabBarActionable.h" #import NS_ASSUME_NONNULL_BEGIN -@interface SplitViewController : UISplitViewController +@interface SplitViewController : UISplitViewController @end diff --git a/Application/Sources/UI/Controllers/SplitViewController.m b/Application/Sources/UI/Controllers/SplitViewController.m index 65afc2af8..86f628e69 100644 --- a/Application/Sources/UI/Controllers/SplitViewController.m +++ b/Application/Sources/UI/Controllers/SplitViewController.m @@ -39,4 +39,25 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI } } +#pragma mark TabBarActionable protocol + +- (void)performActiveTabActionAnimated:(BOOL)animated +{ + void (^performActiveTabAction)(UIViewController *) = ^(UIViewController *viewController) { + if ([viewController conformsToProtocol:@protocol(TabBarActionable)]) { + UIViewController *actionableViewController = (UIViewController *)viewController; + [actionableViewController performActiveTabActionAnimated:animated]; + } + }; + + if (self.viewControllers.count == 2) { + UIViewController *secondaryViewController = self.viewControllers[1]; + performActiveTabAction(secondaryViewController); + } + else { + UIViewController *primaryViewController = self.viewControllers.firstObject; + performActiveTabAction(primaryViewController); + } +} + @end diff --git a/Application/Sources/UI/Controllers/TabBarController.m b/Application/Sources/UI/Controllers/TabBarController.m index c1d48024c..0334b82d3 100755 --- a/Application/Sources/UI/Controllers/TabBarController.m +++ b/Application/Sources/UI/Controllers/TabBarController.m @@ -15,9 +15,9 @@ #import "ProfileViewController.h" #import "PushService.h" #import "RadioChannelsViewController.h" -#import "Scrollable.h" #import "SearchViewController.h" #import "SplitViewController.h" +#import "TabBarActionable.h" #import "UIColor+PlaySRG.h" #import @@ -391,9 +391,9 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI - (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController { if (viewController == self.selectedViewController) { - if ([viewController conformsToProtocol:@protocol(Scrollable)]) { - UIViewController *scrollableViewController = (UIViewController *)viewController; - [scrollableViewController scrollToTopAnimated:YES]; + if ([viewController conformsToProtocol:@protocol(TabBarActionable)]) { + UIViewController *actionableViewController = (UIViewController *)viewController; + [actionableViewController performActiveTabActionAnimated:YES]; } } return YES; diff --git a/Application/Sources/UI/Controllers/TableRequestViewController.h b/Application/Sources/UI/Controllers/TableRequestViewController.h index ed3e71701..62eabd15e 100755 --- a/Application/Sources/UI/Controllers/TableRequestViewController.h +++ b/Application/Sources/UI/Controllers/TableRequestViewController.h @@ -6,7 +6,7 @@ #import "ContentInsets.h" #import "ListRequestViewController.h" -#import "Scrollable.h" +#import "TabBarActionable.h" #import @@ -21,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN * - Automatic display of a load more footer if more data is available * - Clean display of empty tables, whether because of an error or because no items are available */ -@interface TableRequestViewController : ListRequestViewController +@interface TableRequestViewController : ListRequestViewController /** * The table view. diff --git a/Application/Sources/UI/Controllers/TableRequestViewController.m b/Application/Sources/UI/Controllers/TableRequestViewController.m index a03a7b2fb..241552d9f 100755 --- a/Application/Sources/UI/Controllers/TableRequestViewController.m +++ b/Application/Sources/UI/Controllers/TableRequestViewController.m @@ -293,9 +293,9 @@ - (CGFloat)verticalOffsetForEmptyDataSet:(UIScrollView *)scrollView return VerticalOffsetForEmptyDataSet(scrollView); } -#pragma mark Scrollable protocol +#pragma mark TabBarActionable protocol -- (void)scrollToTopAnimated:(BOOL)animated +- (void)performActiveTabActionAnimated:(BOOL)animated { [self.tableView play_scrollToTopAnimated:animated]; } diff --git a/Application/Sources/UI/Helpers/Scrollable.h b/Application/Sources/UI/Helpers/Scrollable.h deleted file mode 100644 index 2e3e98233..000000000 --- a/Application/Sources/UI/Helpers/Scrollable.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright (c) SRG SSR. All rights reserved. -// -// License information is available from the LICENSE file. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - * Protocol for view controllers with 'scroll to the top' support. - */ -@protocol Scrollable - -- (void)scrollToTopAnimated:(BOOL)animated; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Application/Sources/UI/Helpers/TabBarActionable.h b/Application/Sources/UI/Helpers/TabBarActionable.h new file mode 100644 index 000000000..2f61f62b3 --- /dev/null +++ b/Application/Sources/UI/Helpers/TabBarActionable.h @@ -0,0 +1,23 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Protocol implemented by view controllers to respond to tab bar actions. + */ +@protocol TabBarActionable + +/** + * Called when the currently active tab has been tapped again. + */ +- (void)performActiveTabActionAnimated:(BOOL)animated; + +@end + +NS_ASSUME_NONNULL_END diff --git a/PlaySRG.xcodeproj/project.pbxproj b/PlaySRG.xcodeproj/project.pbxproj index 2deeef918..ae2737093 100644 --- a/PlaySRG.xcodeproj/project.pbxproj +++ b/PlaySRG.xcodeproj/project.pbxproj @@ -2143,7 +2143,7 @@ 6FB0BB3C20AEF5C4007C5D87 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Onboarding.strings; sourceTree = ""; }; 6FB0BB3F20AEF5D1007C5D87 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Onboarding.strings; sourceTree = ""; }; 6FB0BB4220AEF5DC007C5D87 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Onboarding.strings; sourceTree = ""; }; - 6FB340D823E1A21500BC83BF /* Scrollable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Scrollable.h; sourceTree = ""; }; + 6FB340D823E1A21500BC83BF /* TabBarActionable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TabBarActionable.h; sourceTree = ""; }; 6FB3C1A51DBCE783008160B4 /* MediaPreviewViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaPreviewViewController.h; sourceTree = ""; }; 6FB3C1A61DBCE783008160B4 /* MediaPreviewViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MediaPreviewViewController.m; sourceTree = ""; }; 6FB3C1A71DBCE783008160B4 /* MediaPreviewViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MediaPreviewViewController.storyboard; sourceTree = ""; }; @@ -3254,6 +3254,7 @@ 6FFCB62324504C6C00D16466 /* SwimlaneCollectionViewLayout.m */, 6FF4D4DE23D5B07E008B981A /* UIVisualEffectView+PlaySRG.h */, 6FF4D4DF23D5B07E008B981A /* UIVisualEffectView+PlaySRG.m */, + 6FB340D823E1A21500BC83BF /* TabBarActionable.h */, ); path = Helpers; sourceTree = ""; From b8106b7287d7a36694d718180bbeaecfab112fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Sat, 25 Apr 2020 19:21:37 +0200 Subject: [PATCH 31/62] Improve accessibilty with VoiceOver --- Application/Sources/Library/ProfileViewController.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Application/Sources/Library/ProfileViewController.m b/Application/Sources/Library/ProfileViewController.m index 56a9dd1cb..07e662b84 100755 --- a/Application/Sources/Library/ProfileViewController.m +++ b/Application/Sources/Library/ProfileViewController.m @@ -259,6 +259,10 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI self.splitViewController.viewControllers = viewControllers.copy; [self updateSelection]; } + + if (! self.splitViewController.collapsed) { + UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, viewController.view); + } return YES; } else { From 9f3aa873a6adfc7f540f8f5cc8e16395bd2f703a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Mon, 27 Apr 2020 07:52:00 +0200 Subject: [PATCH 32/62] Preserve selection when the table view is reloaded --- Application/Sources/Library/ProfileViewController.m | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Application/Sources/Library/ProfileViewController.m b/Application/Sources/Library/ProfileViewController.m index 07e662b84..b9a411b1f 100755 --- a/Application/Sources/Library/ProfileViewController.m +++ b/Application/Sources/Library/ProfileViewController.m @@ -142,18 +142,23 @@ - (void)updateForContentSizeCategory { [super updateForContentSizeCategory]; - [self.tableView reloadData]; + [self reloadTableView]; } #pragma mark Data +- (void)reloadTableView +{ + [self.tableView reloadData]; + [self updateSelection]; +} + - (void)reloadData { self.sectionInfos = ApplicationSectionInfo.profileApplicationSectionInfos; - [self.tableView reloadData]; + [self reloadTableView]; dispatch_async(dispatch_get_main_queue(), ^{ - [self updateSelection]; [self.tableView flashScrollIndicators]; }); } @@ -378,7 +383,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath [NotificationsViewController openNotification:notification fromViewController:self]; // Update the cell dot right away - [tableView reloadData]; + [self reloadTableView]; } else { ApplicationSectionInfo *applicationSectionInfo = self.sectionInfos[indexPath.row]; From 311d1104a5ecc8a5cd79bdd1281dca7df7ecba88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 1 May 2020 15:20:46 +0200 Subject: [PATCH 33/62] Hide notification previews in expanded mode --- Application/Sources/Helpers/ApplicationSectionInfo.h | 4 ++-- Application/Sources/Helpers/ApplicationSectionInfo.m | 12 +++++++----- Application/Sources/Library/ProfileViewController.m | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Application/Sources/Helpers/ApplicationSectionInfo.h b/Application/Sources/Helpers/ApplicationSectionInfo.h index 10571470d..be0b46fbc 100755 --- a/Application/Sources/Helpers/ApplicationSectionInfo.h +++ b/Application/Sources/Helpers/ApplicationSectionInfo.h @@ -32,9 +32,9 @@ OBJC_EXPORT ApplicationSectionOptionKey const ApplicationSectionOptionShowByDate + (ApplicationSectionInfo *)applicationSectionInfoWithApplicationSection:(ApplicationSection)applicationSection radioChannel:(nullable RadioChannel *)radioChannel options:(nullable NSDictionary *)options; /** - * Return the profile section infos available for the current configuration. + * Return the profile section infos available for the current configuration (with optional inlined notification preview). */ -@property (class, nonatomic, readonly) NSArray *profileApplicationSectionInfos; ++ (NSArray *)profileApplicationSectionInfosWithNotificationPreview:(BOOL)notificationPreview; /** * Properties. diff --git a/Application/Sources/Helpers/ApplicationSectionInfo.m b/Application/Sources/Helpers/ApplicationSectionInfo.m index 513f8f2f8..cb211af9e 100755 --- a/Application/Sources/Helpers/ApplicationSectionInfo.m +++ b/Application/Sources/Helpers/ApplicationSectionInfo.m @@ -50,17 +50,19 @@ + (ApplicationSectionInfo *)applicationSectionInfoWithNotification:(Notification options:@{ ApplicationSectionOptionNotificationKey : notification }]; } -+ (NSArray *)profileApplicationSectionInfos ++ (NSArray *)profileApplicationSectionInfosWithNotificationPreview:(BOOL)notificationPreview { NSMutableArray *sectionInfos = [NSMutableArray array]; if (@available(iOS 10, *)) { if (PushService.sharedService.enabled) { [sectionInfos addObject:[self applicationSectionInfoWithApplicationSection:ApplicationSectionNotifications radioChannel:nil]]; - NSArray *unreadNotifications = Notification.unreadNotifications; - NSArray *previewNotifications = [unreadNotifications subarrayWithRange:NSMakeRange(0, MIN(3, unreadNotifications.count))]; - for (Notification *notification in previewNotifications) { - [sectionInfos addObject:[self applicationSectionInfoWithNotification:notification]]; + if (notificationPreview) { + NSArray *unreadNotifications = Notification.unreadNotifications; + NSArray *previewNotifications = [unreadNotifications subarrayWithRange:NSMakeRange(0, MIN(3, unreadNotifications.count))]; + for (Notification *notification in previewNotifications) { + [sectionInfos addObject:[self applicationSectionInfoWithNotification:notification]]; + } } } } diff --git a/Application/Sources/Library/ProfileViewController.m b/Application/Sources/Library/ProfileViewController.m index b9a411b1f..a9e3f9936 100755 --- a/Application/Sources/Library/ProfileViewController.m +++ b/Application/Sources/Library/ProfileViewController.m @@ -155,7 +155,7 @@ - (void)reloadTableView - (void)reloadData { - self.sectionInfos = ApplicationSectionInfo.profileApplicationSectionInfos; + self.sectionInfos = [ApplicationSectionInfo profileApplicationSectionInfosWithNotificationPreview:self.splitViewController.collapsed]; [self reloadTableView]; dispatch_async(dispatch_get_main_queue(), ^{ From 1955123e3bfaee78d1d315afd37fe70e938758d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 1 May 2020 15:29:34 +0200 Subject: [PATCH 34/62] Avoid download animation being stopped when the download cell is selected --- Application/Sources/Library/ProfileTableViewCell.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Application/Sources/Library/ProfileTableViewCell.m b/Application/Sources/Library/ProfileTableViewCell.m index 784930979..de3414084 100755 --- a/Application/Sources/Library/ProfileTableViewCell.m +++ b/Application/Sources/Library/ProfileTableViewCell.m @@ -78,6 +78,13 @@ - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated [self updateIconImageViewAnimation]; } +- (void)setSelected:(BOOL)selected animated:(BOOL)animated +{ + [super setSelected:selected animated:animated]; + + [self updateIconImageViewAnimation]; +} + #pragma mark User interface - (void)updateIconImageViewAnimation From 604de2af47cce41199063c191d6aacad5dee24f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 1 May 2020 15:34:37 +0200 Subject: [PATCH 35/62] Use pop style for preview action on iPad as well This also avoids the user doing another interaction while transitioning (e.g. opening the player, which would lead to two players being modally displayed). This was previously possible with the dismissal animation. --- Application/Sources/UI/Controllers/BaseViewController.m | 1 + 1 file changed, 1 insertion(+) diff --git a/Application/Sources/UI/Controllers/BaseViewController.m b/Application/Sources/UI/Controllers/BaseViewController.m index 769071315..0e62fa4ba 100755 --- a/Application/Sources/UI/Controllers/BaseViewController.m +++ b/Application/Sources/UI/Controllers/BaseViewController.m @@ -346,6 +346,7 @@ - (UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction - (void)contextMenuInteraction:(UIContextMenuInteraction *)interaction willPerformPreviewActionForMenuWithConfiguration:(UIContextMenuConfiguration *)configuration animator:(id)animator API_AVAILABLE(ios(13.0)) { UIViewController *viewController = animator.previewViewController; + animator.preferredCommitStyle = UIContextMenuInteractionCommitStylePop; [animator addCompletion:^{ if ([viewController isKindOfClass:MediaPreviewViewController.class]) { MediaPreviewViewController *mediaPreviewViewController = (MediaPreviewViewController *)viewController; From 9bc9ede1bbdb410427c1e5edfc3c6acabb8d8d60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 1 May 2020 15:47:47 +0200 Subject: [PATCH 36/62] Factor out tab view controller creation --- .../UI/Controllers/NavigationController.m | 2 +- .../Sources/UI/Controllers/TabBarController.m | 185 +++++++++++------- 2 files changed, 114 insertions(+), 73 deletions(-) diff --git a/Application/Sources/UI/Controllers/NavigationController.m b/Application/Sources/UI/Controllers/NavigationController.m index 8dbaf8ea1..c90476b29 100755 --- a/Application/Sources/UI/Controllers/NavigationController.m +++ b/Application/Sources/UI/Controllers/NavigationController.m @@ -187,7 +187,7 @@ - (void)performActiveTabActionAnimated:(BOOL)animated } } else { - // Natively performed when a navigation controller is directly embedd in a tab bar controller, but here triggered + // Natively performed when a navigation controller is directly embedded in a tab bar controller, but here triggered // explicitly for all other kinds of embedding as well (e.g. tab bar -> split view -> navigation). [self popToRootViewControllerAnimated:animated]; } diff --git a/Application/Sources/UI/Controllers/TabBarController.m b/Application/Sources/UI/Controllers/TabBarController.m index 0334b82d3..14e9cdbf1 100755 --- a/Application/Sources/UI/Controllers/TabBarController.m +++ b/Application/Sources/UI/Controllers/TabBarController.m @@ -44,86 +44,26 @@ - (instancetype)init if (self = [super init]) { self.delegate = self; - ApplicationConfiguration *applicationConfiguration = ApplicationConfiguration.sharedApplicationConfiguration; - NSMutableArray *viewControllers = NSMutableArray.array; - // Videos tab - ApplicationSectionInfo *videosApplicationSectionInfo = [ApplicationSectionInfo applicationSectionInfoWithApplicationSection:ApplicationSectionOverview radioChannel:nil]; - UIViewController *videosViewController = [[HomeViewController alloc] initWithApplicationSectionInfo:videosApplicationSectionInfo homeSections:applicationConfiguration.videoHomeSections]; - videosViewController.title = NSLocalizedString(@"Videos", @"Title displayed at the top of the video view"); - - UITabBarItem *videosTabBarItem = [[UITabBarItem alloc] initWithTitle:videosViewController.title image:[UIImage imageNamed:@"videos-24"] tag:TabBarItemIdentifierVideos]; - videosTabBarItem.accessibilityIdentifier = AccessibilityIdentifierVideosTabBarItem; + UIViewController *videosTabViewController = [self videosTabViewController]; + [viewControllers addObject:videosTabViewController]; - NavigationController *videosNavigationController = [[NavigationController alloc] initWithRootViewController:videosViewController]; - videosNavigationController.tabBarItem = videosTabBarItem; - [viewControllers addObject:videosNavigationController]; - - // Audios tab - NSArray *radioChannels = applicationConfiguration.radioChannels; - if (radioChannels.count > 1) { - UIViewController *radioChannelsViewController = [[RadioChannelsViewController alloc] initWithRadioChannels:radioChannels]; - - UITabBarItem *audiosTabBarItem = [[UITabBarItem alloc] initWithTitle:radioChannelsViewController.title image:[UIImage imageNamed:@"audios-24"] tag:TabBarItemIdentifierAudios]; - audiosTabBarItem.accessibilityIdentifier = AccessibilityIdentifierAudiosTabBarItem; - - NavigationController *audiosNavigationController = [[NavigationController alloc] initWithRootViewController:radioChannelsViewController]; - audiosNavigationController.tabBarItem = audiosTabBarItem; - [viewControllers addObject:audiosNavigationController]; - } - else if (radioChannels.count == 1) { - RadioChannel *radioChannel = radioChannels.firstObject; - ApplicationSectionInfo *audiosApplicationSectionInfo = [ApplicationSectionInfo applicationSectionInfoWithApplicationSection:ApplicationSectionOverview radioChannel:radioChannel]; - UIViewController *audiosViewController = [[HomeViewController alloc] initWithApplicationSectionInfo:audiosApplicationSectionInfo homeSections:radioChannel.homeSections]; - audiosViewController.title = NSLocalizedString(@"Audios", @"Title displayed at the top of the audio view"); - - UITabBarItem *audiosTabBarItem = [[UITabBarItem alloc] initWithTitle:audiosViewController.title image:[UIImage imageNamed:@"audios-24"] tag:TabBarItemIdentifierAudios]; - audiosTabBarItem.accessibilityIdentifier = AccessibilityIdentifierAudiosTabBarItem; - - NavigationController *audiosNavigationController = [[NavigationController alloc] initWithRootViewController:audiosViewController]; - audiosNavigationController.tabBarItem = audiosTabBarItem; - [audiosNavigationController updateWithRadioChannel:radioChannel animated:NO]; - [viewControllers addObject:audiosNavigationController]; + UIViewController *audiosTabViewController = [self audiosTabViewController]; + if (audiosTabViewController) { + [viewControllers addObject:audiosTabViewController]; } - // Live tab - NSArray *liveHomeSections = ApplicationConfiguration.sharedApplicationConfiguration.liveHomeSections; - if (liveHomeSections.count != 0) { - ApplicationSectionInfo *liveApplicationSectionInfo = [ApplicationSectionInfo applicationSectionInfoWithApplicationSection:ApplicationSectionLive radioChannel:nil]; - UIViewController *liveHomeViewController = [[HomeViewController alloc] initWithApplicationSectionInfo:liveApplicationSectionInfo homeSections:liveHomeSections]; - liveHomeViewController.title = NSLocalizedString(@"Livestreams", @"Title displayed at the top of the livestream view"); - - UITabBarItem *liveTabBarItem = [[UITabBarItem alloc] initWithTitle:liveHomeViewController.title image:[UIImage imageNamed:@"livestreams-24"] tag:TabBarItemIdentifierLivestreams]; - liveTabBarItem.accessibilityIdentifier = AccessibilityIdentifierLivestreamsTabBarItem; - - NavigationController *liveNavigationController = [[NavigationController alloc] initWithRootViewController:liveHomeViewController]; - liveNavigationController.tabBarItem = liveTabBarItem; - [viewControllers addObject:liveNavigationController]; + UIViewController *livestreamsTabViewController = [self livestreamsTabViewController]; + if (livestreamsTabViewController) { + [viewControllers addObject:livestreamsTabViewController]; } - // Search tab - UIViewController *searchViewController = [[SearchViewController alloc] init]; - - UITabBarItem *searchTabBarItem = [[UITabBarItem alloc] initWithTitle:searchViewController.title image:[UIImage imageNamed:@"search-24"] tag:TabBarItemIdentifierSearch]; - searchTabBarItem.accessibilityIdentifier = AccessibilityIdentifierSearchTabBarItem; - - NavigationController *searchNavigationController = [[NavigationController alloc] initWithRootViewController:searchViewController]; - searchNavigationController.tabBarItem = searchTabBarItem; - [viewControllers addObject:searchNavigationController]; - - // Profile tab - UIViewController *profileViewController = [[ProfileViewController alloc] init]; - NavigationController *profileNavigationController = [[NavigationController alloc] initWithRootViewController:profileViewController]; + UIViewController *searchTabViewController = [self searchTabViewController]; + [viewControllers addObject:searchTabViewController]; - UITabBarItem *profileTabBarItem = [[UITabBarItem alloc] initWithTitle:profileViewController.title image:[UIImage imageNamed:@"profile-24"] tag:TabBarItemIdentifierProfile]; - profileTabBarItem.accessibilityIdentifier = AccessibilityIdentifierProfileTabBarItem; - - SplitViewController *profileSplitViewController = [[SplitViewController alloc] init]; - profileSplitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible; - profileSplitViewController.viewControllers = @[ profileNavigationController ]; - profileSplitViewController.tabBarItem = profileTabBarItem; - [viewControllers addObject:profileSplitViewController]; + UIViewController *profileTabViewController = [self profileTabViewController]; + [viewControllers addObject:profileTabViewController]; self.viewControllers = viewControllers.copy; @@ -264,6 +204,107 @@ - (void)setSelectedViewController:(UIViewController *)selectedViewController ApplicationSettingSetLastOpenedTabBarItemIdentifier(selectedViewController.tabBarItem.tag); } +#pragma mark View controllers + +- (UIViewController *)videosTabViewController +{ + ApplicationConfiguration *applicationConfiguration = ApplicationConfiguration.sharedApplicationConfiguration; + + ApplicationSectionInfo *videosApplicationSectionInfo = [ApplicationSectionInfo applicationSectionInfoWithApplicationSection:ApplicationSectionOverview radioChannel:nil]; + UIViewController *videosViewController = [[HomeViewController alloc] initWithApplicationSectionInfo:videosApplicationSectionInfo homeSections:applicationConfiguration.videoHomeSections]; + videosViewController.title = NSLocalizedString(@"Videos", @"Title displayed at the top of the video view"); + + UITabBarItem *videosTabBarItem = [[UITabBarItem alloc] initWithTitle:videosViewController.title image:[UIImage imageNamed:@"videos-24"] tag:TabBarItemIdentifierVideos]; + videosTabBarItem.accessibilityIdentifier = AccessibilityIdentifierVideosTabBarItem; + + NavigationController *videosNavigationController = [[NavigationController alloc] initWithRootViewController:videosViewController]; + videosNavigationController.tabBarItem = videosTabBarItem; + return videosNavigationController; +} + +- (UIViewController *)audiosTabViewController +{ + ApplicationConfiguration *applicationConfiguration = ApplicationConfiguration.sharedApplicationConfiguration; + + NSArray *radioChannels = applicationConfiguration.radioChannels; + if (radioChannels.count > 1) { + UIViewController *radioChannelsViewController = [[RadioChannelsViewController alloc] initWithRadioChannels:radioChannels]; + + UITabBarItem *audiosTabBarItem = [[UITabBarItem alloc] initWithTitle:radioChannelsViewController.title image:[UIImage imageNamed:@"audios-24"] tag:TabBarItemIdentifierAudios]; + audiosTabBarItem.accessibilityIdentifier = AccessibilityIdentifierAudiosTabBarItem; + + NavigationController *audiosNavigationController = [[NavigationController alloc] initWithRootViewController:radioChannelsViewController]; + audiosNavigationController.tabBarItem = audiosTabBarItem; + return audiosNavigationController; + } + else if (radioChannels.count == 1) { + RadioChannel *radioChannel = radioChannels.firstObject; + ApplicationSectionInfo *audiosApplicationSectionInfo = [ApplicationSectionInfo applicationSectionInfoWithApplicationSection:ApplicationSectionOverview radioChannel:radioChannel]; + UIViewController *audiosViewController = [[HomeViewController alloc] initWithApplicationSectionInfo:audiosApplicationSectionInfo homeSections:radioChannel.homeSections]; + audiosViewController.title = NSLocalizedString(@"Audios", @"Title displayed at the top of the audio view"); + + UITabBarItem *audiosTabBarItem = [[UITabBarItem alloc] initWithTitle:audiosViewController.title image:[UIImage imageNamed:@"audios-24"] tag:TabBarItemIdentifierAudios]; + audiosTabBarItem.accessibilityIdentifier = AccessibilityIdentifierAudiosTabBarItem; + + NavigationController *audiosNavigationController = [[NavigationController alloc] initWithRootViewController:audiosViewController]; + audiosNavigationController.tabBarItem = audiosTabBarItem; + [audiosNavigationController updateWithRadioChannel:radioChannel animated:NO]; + return audiosNavigationController; + } + else { + return nil; + } +} + +- (UIViewController *)livestreamsTabViewController +{ + ApplicationConfiguration *applicationConfiguration = ApplicationConfiguration.sharedApplicationConfiguration; + + NSArray *liveHomeSections = applicationConfiguration.liveHomeSections; + if (liveHomeSections.count != 0) { + ApplicationSectionInfo *liveApplicationSectionInfo = [ApplicationSectionInfo applicationSectionInfoWithApplicationSection:ApplicationSectionLive radioChannel:nil]; + UIViewController *liveHomeViewController = [[HomeViewController alloc] initWithApplicationSectionInfo:liveApplicationSectionInfo homeSections:liveHomeSections]; + liveHomeViewController.title = NSLocalizedString(@"Livestreams", @"Title displayed at the top of the livestream view"); + + UITabBarItem *liveTabBarItem = [[UITabBarItem alloc] initWithTitle:liveHomeViewController.title image:[UIImage imageNamed:@"livestreams-24"] tag:TabBarItemIdentifierLivestreams]; + liveTabBarItem.accessibilityIdentifier = AccessibilityIdentifierLivestreamsTabBarItem; + + NavigationController *liveNavigationController = [[NavigationController alloc] initWithRootViewController:liveHomeViewController]; + liveNavigationController.tabBarItem = liveTabBarItem; + return liveNavigationController; + } + else { + return nil; + } +} + +- (UIViewController *)searchTabViewController +{ + UIViewController *searchViewController = [[SearchViewController alloc] init]; + + UITabBarItem *searchTabBarItem = [[UITabBarItem alloc] initWithTitle:searchViewController.title image:[UIImage imageNamed:@"search-24"] tag:TabBarItemIdentifierSearch]; + searchTabBarItem.accessibilityIdentifier = AccessibilityIdentifierSearchTabBarItem; + + NavigationController *searchNavigationController = [[NavigationController alloc] initWithRootViewController:searchViewController]; + searchNavigationController.tabBarItem = searchTabBarItem; + return searchNavigationController; +} + +- (UIViewController *)profileTabViewController +{ + UIViewController *profileViewController = [[ProfileViewController alloc] init]; + NavigationController *profileNavigationController = [[NavigationController alloc] initWithRootViewController:profileViewController]; + + UITabBarItem *profileTabBarItem = [[UITabBarItem alloc] initWithTitle:profileViewController.title image:[UIImage imageNamed:@"profile-24"] tag:TabBarItemIdentifierProfile]; + profileTabBarItem.accessibilityIdentifier = AccessibilityIdentifierProfileTabBarItem; + + SplitViewController *profileSplitViewController = [[SplitViewController alloc] init]; + profileSplitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible; + profileSplitViewController.viewControllers = @[ profileNavigationController ]; + profileSplitViewController.tabBarItem = profileTabBarItem; + return profileSplitViewController; +} + #pragma mark Layout - (void)updateLayoutAnimated:(BOOL)animated From 4cd8d4c1d4e904cda16a8dc63f09918225389d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 1 May 2020 16:05:14 +0200 Subject: [PATCH 37/62] Improve profile view margins --- Application/Sources/Library/ProfileViewController.m | 2 +- Application/Sources/UI/Controllers/SplitViewController.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Application/Sources/Library/ProfileViewController.m b/Application/Sources/Library/ProfileViewController.m index a9e3f9936..602daf0f1 100755 --- a/Application/Sources/Library/ProfileViewController.m +++ b/Application/Sources/Library/ProfileViewController.m @@ -299,7 +299,7 @@ - (void)updateSelection - (UIEdgeInsets)play_paddingContentInsets { - return UIEdgeInsetsZero; + return SRGIdentityService.currentIdentityService ? UIEdgeInsetsZero : LayoutStandardTableViewPaddingInsets; } #pragma mark PlayApplicationNavigation protocol diff --git a/Application/Sources/UI/Controllers/SplitViewController.h b/Application/Sources/UI/Controllers/SplitViewController.h index 22a573bee..58e5a599c 100644 --- a/Application/Sources/UI/Controllers/SplitViewController.h +++ b/Application/Sources/UI/Controllers/SplitViewController.h @@ -11,6 +11,9 @@ NS_ASSUME_NONNULL_BEGIN +/** + * Lightweight split view controller subclass with standard behavior. + */ @interface SplitViewController : UISplitViewController @end From c6af083c69fe92b288f40666efa9a3bdc38fa976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 1 May 2020 16:13:13 +0200 Subject: [PATCH 38/62] Improve selection update implementation --- .../Sources/Library/ProfileViewController.m | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Application/Sources/Library/ProfileViewController.m b/Application/Sources/Library/ProfileViewController.m index 602daf0f1..c3b90b31d 100755 --- a/Application/Sources/Library/ProfileViewController.m +++ b/Application/Sources/Library/ProfileViewController.m @@ -108,6 +108,8 @@ - (void)viewWillAppear:(BOOL)animated // Ensure latest notifications are displayed [self reloadData]; + // On iPad where split screen can be used, load the secondary view afterwards (if loaded too early it will be collapsed + // automatically onto the primary at startup for narrow layouts, which is not what we want). if ([self play_isMovingToParentViewController] && UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) { [self openApplicationSectionInfo:self.sectionInfos.firstObject interactive:NO animated:NO]; } @@ -228,6 +230,7 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI return NO; } + // Do not reload a section if already the current one if (! self.splitViewController.collapsed && [applicationSectionInfo isEqual:self.currentSectionInfo]) { return YES; } @@ -265,6 +268,7 @@ - (BOOL)openApplicationSectionInfo:(ApplicationSectionInfo *)applicationSectionI [self updateSelection]; } + // Transfer the VoiceOver focus automatically, as is for example done in the Settings application. if (! self.splitViewController.collapsed) { UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, viewController.view); } @@ -282,12 +286,16 @@ - (void)updateSelection } NSUInteger index = [self.sectionInfos indexOfObject:self.currentSectionInfo]; - if (index == NSNotFound) { - return; + if (index != NSNotFound) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0]; + [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; + } + else { + NSIndexPath *indexPath = self.tableView.indexPathForSelectedRow; + if (indexPath) { + [self.tableView deselectRowAtIndexPath:indexPath animated:NO]; + } } - - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0]; - [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } #pragma mark ContentInsets protocol From 023eb279edd13b84d750c39431b5dfe7f5e53a30 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Bertholon Date: Thu, 7 May 2020 21:19:35 +0200 Subject: [PATCH 39/62] Fix typo --- Application/Sources/UI/Controllers/PageViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Application/Sources/UI/Controllers/PageViewController.m b/Application/Sources/UI/Controllers/PageViewController.m index fcfe79545..9739ad8c2 100755 --- a/Application/Sources/UI/Controllers/PageViewController.m +++ b/Application/Sources/UI/Controllers/PageViewController.m @@ -244,7 +244,7 @@ - (void)tabBar:(MDCTabBar *)tabBar didSelectItem:(UITabBarItem *)item return self.pageViewController ? @[self.pageViewController] : @[]; } -#pragma mark Scrollable protocol +#pragma mark TabBarActionable protocol - (void)performActiveTabActionAnimated:(BOOL)animated { From eaf4f8b48dd871edc631b44724ab3feb8d90eafb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 24 Apr 2020 17:12:46 +0200 Subject: [PATCH 40/62] Add functions to save and restore last livestreams --- Application/Sources/Settings/ApplicationSettings.h | 6 ++++++ Application/Sources/Settings/ApplicationSettings.m | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Application/Sources/Settings/ApplicationSettings.h b/Application/Sources/Settings/ApplicationSettings.h index e6a0d5158..84480ccb8 100755 --- a/Application/Sources/Settings/ApplicationSettings.h +++ b/Application/Sources/Settings/ApplicationSettings.h @@ -72,6 +72,12 @@ OBJC_EXPORT BOOL ApplicationSettingBackgroundVideoPlaybackEnabled(void); OBJC_EXPORT BOOL ApplicationSettingSubtitleAvailabilityDisplayed(void); OBJC_EXPORT BOOL ApplicationSettingAudioDescriptionAvailabilityDisplayed(void); +OBJC_EXPORT NSString * _Nullable ApplicationSettingLastSelectedTVLivestreamURN(void); +OBJC_EXPORT void ApplicationSettingSetSetLastSelectedTVLivestreamURN(NSString * _Nullable mediaURN); + +OBJC_EXPORT NSString * _Nullable ApplicationSettingLastSelectedRadioLivestreamURN(void); +OBJC_EXPORT void ApplicationSettingSetSetLastSelectedRadioLivestreamURN(NSString * _Nullable mediaURN); + OBJC_EXPORT NSString * _Nullable ApplicationSettingSelectedLivestreamURNForChannelUid(NSString * _Nullable channelUid); OBJC_EXPORT void ApplicationSettingSetSelectedLivestreamURNForChannelUid(NSString * channelUid, NSString * _Nullable mediaURN); diff --git a/Application/Sources/Settings/ApplicationSettings.m b/Application/Sources/Settings/ApplicationSettings.m index df4b5c26f..020324b4b 100755 --- a/Application/Sources/Settings/ApplicationSettings.m +++ b/Application/Sources/Settings/ApplicationSettings.m @@ -31,6 +31,8 @@ NSString * const PlaySRGSettingLastOpenedRadioChannelUid = @"PlaySRGSettingLastOpenedRadioChannelUid"; NSString * const PlaySRGSettingLastOpenedTabBarItem = @"PlaySRGSettingLastOpenedTabBarItem"; NSString * const PlaySRGSettingSelectedLivestreamURNForChannels = @"PlaySRGSettingSelectedLiveStreamURNForChannels"; +NSString * const PlaySRGSettingSelectedTVLivestreamURN = @"PlaySRGSettingSelectedTVLivestreamURN"; +NSString * const PlaySRGSettingSelectedRadioLivestreamURN = @"PlaySRGSettingSelectedRadioLivestreamURN"; NSString * const PlaySRGSettingServiceURL = @"PlaySRGSettingServiceURL"; NSString * const PlaySRGSettingUserLocation = @"PlaySRGSettingUserLocation"; @@ -216,7 +218,7 @@ BOOL ApplicationSettingAudioDescriptionAvailabilityDisplayed(void) void ApplicationSettingSetSelectedLivestreamURNForChannelUid(NSString *channelUid, NSString *mediaURN) { if (channelUid) { - NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults;; + NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults; NSDictionary *selectedLivestreamURNForChannels = [userDefaults dictionaryForKey:PlaySRGSettingSelectedLivestreamURNForChannels]; NSMutableDictionary *mutableSelectedLivestreamURNForChannels = selectedLivestreamURNForChannels.mutableCopy ?: NSMutableDictionary.new; From 2cfaa90989a596198db1c3cc28ae9c636e7c477e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 24 Apr 2020 17:22:47 +0200 Subject: [PATCH 41/62] Display last used livestreams first --- .../Sources/Home/HomeMediaListTableViewCell.m | 10 +++++++ Application/Sources/Home/HomeSectionInfo.m | 28 +++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/Application/Sources/Home/HomeMediaListTableViewCell.m b/Application/Sources/Home/HomeMediaListTableViewCell.m index 1479a32a6..5f4832a75 100755 --- a/Application/Sources/Home/HomeMediaListTableViewCell.m +++ b/Application/Sources/Home/HomeMediaListTableViewCell.m @@ -6,6 +6,7 @@ #import "HomeMediaListTableViewCell.h" +#import "ApplicationSettings.h" #import "HomeLiveMediaCollectionViewCell.h" #import "HomeMediaCollectionHeaderView.h" #import "HomeMediaCollectionViewCell.h" @@ -213,6 +214,15 @@ - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPa { if (! [self isEmpty]) { SRGMedia *media = self.homeSectionInfo.items[indexPath.row]; + + HomeSection homeSection = self.homeSectionInfo.homeSection; + if (homeSection == HomeSectionTVLive) { + ApplicationSettingSetSetLastSelectedTVLivestreamURN(media.URN); + } + else if (homeSection == HomeSectionRadioLive) { + ApplicationSettingSetSetLastSelectedRadioLivestreamURN(media.URN); + } + [self.nearestViewController play_presentMediaPlayerWithMedia:media position:nil airPlaySuggestions:YES fromPushNotification:NO animated:YES completion:nil]; } } diff --git a/Application/Sources/Home/HomeSectionInfo.m b/Application/Sources/Home/HomeSectionInfo.m index 611783e6e..1b1ec7f35 100755 --- a/Application/Sources/Home/HomeSectionInfo.m +++ b/Application/Sources/Home/HomeSectionInfo.m @@ -17,6 +17,23 @@ #import #import +static NSArray *HomeSectionReorderedMedias(NSArray *medias, NSString *firstURN) +{ + NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(SRGMedia * _Nullable media, NSDictionary * _Nullable bindings) { + return [media.URN isEqualToString:firstURN]; + }]; + SRGMedia *firstMedia = [medias filteredArrayUsingPredicate:predicate].firstObject; + if (firstMedia) { + NSMutableArray *reorderedMedias = medias.mutableCopy; + [reorderedMedias removeObject:firstMedia]; + [reorderedMedias insertObject:firstMedia atIndex:0]; + return reorderedMedias.copy; + } + else { + return medias; + } +} + @interface HomeSectionInfo () @property (nonatomic) HomeSection homeSection; @@ -242,7 +259,9 @@ - (void)refreshWithRequestQueue:(SRGRequestQueue *)requestQueue page:(SRGPage *) case HomeSectionTVLive: { SRGBaseRequest *request = [SRGDataProvider.currentDataProvider tvLivestreamsForVendor:vendor withCompletionBlock:^(NSArray * _Nullable medias, NSHTTPURLResponse * _Nullable HTTPResponse, NSError * _Nullable error) { [requestQueue reportError:error]; - paginatedItemListCompletionBlock(medias, [SRGPage new] /* The request does not support pagination, but we need to return a page */, nil, HTTPResponse, error); + + NSString *lastSelectedURN = ApplicationSettingLastSelectedTVLivestreamURN(); + paginatedItemListCompletionBlock(HomeSectionReorderedMedias(medias, lastSelectedURN), [SRGPage new] /* The request does not support pagination, but we need to return a page */, nil, HTTPResponse, error); }]; [requestQueue addRequest:request resume:YES]; break; @@ -330,14 +349,17 @@ - (void)refreshWithRequestQueue:(SRGRequestQueue *)requestQueue page:(SRGPage *) if (self.identifier) { SRGRequest *request = [SRGDataProvider.currentDataProvider radioLivestreamsForVendor:vendor channelUid:self.identifier withCompletionBlock:^(NSArray * _Nullable medias, NSHTTPURLResponse * _Nullable HTTPResponse, NSError * _Nullable error) { [requestQueue reportError:error]; - paginatedItemListCompletionBlock(medias, [SRGPage new] /* The request does not support pagination, but we need to return a page */, nil, HTTPResponse, error); + + NSString *lastSelectedURN = ApplicationSettingLastSelectedRadioLivestreamURN(); + paginatedItemListCompletionBlock(HomeSectionReorderedMedias(medias, lastSelectedURN), [SRGPage new] /* The request does not support pagination, but we need to return a page */, nil, HTTPResponse, error); }]; [requestQueue addRequest:request resume:YES]; } else { [self refreshRadioLivestreamsForVendor:vendor withRequestQueue:requestQueue completionBlock:^(NSArray * _Nullable medias, NSHTTPURLResponse * _Nullable HTTPResponse, NSError * _Nullable error) { // Error reporting is done by the refresh method directly, do not report twice here - paginatedItemListCompletionBlock(medias, [SRGPage new] /* The request does not support pagination, but we need to return a page */, nil, HTTPResponse, error); + NSString *lastSelectedURN = ApplicationSettingLastSelectedRadioLivestreamURN(); + paginatedItemListCompletionBlock(HomeSectionReorderedMedias(medias, lastSelectedURN), [SRGPage new] /* The request does not support pagination, but we need to return a page */, nil, HTTPResponse, error); }]; } break; From d7a0989b78da60713ab1099730191465cd6facd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 24 Apr 2020 18:50:45 +0200 Subject: [PATCH 42/62] Highlight recently played livestreams --- .../Play RTS/fr.lproj/Localizable.strings | 2 ++ .../Sources/Home/HomeMediaListTableViewCell.m | 9 ++++++--- .../Views/HomeLiveMediaCollectionViewCell.m | 12 ++++++++++++ .../Views/HomeLiveMediaCollectionViewCell.xib | 19 +++++++++++++++++-- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/Application/Resources/Apps/Play RTS/fr.lproj/Localizable.strings b/Application/Resources/Apps/Play RTS/fr.lproj/Localizable.strings index d316213b6..7edf7131c 100644 --- a/Application/Resources/Apps/Play RTS/fr.lproj/Localizable.strings +++ b/Application/Resources/Apps/Play RTS/fr.lproj/Localizable.strings @@ -353,6 +353,8 @@ /* Title displayed when no media is being played on the connected Google Cast receiver (name unknown) */ "Receiver is idle." = "Le récepteur est en attente."; +"Recently played" = "Joué récemment"; + /* Hint displayed when no history is available */ "Recently played medias will be displayed here" = "Les contenus récemment consultés apparaîtront ici"; diff --git a/Application/Sources/Home/HomeMediaListTableViewCell.m b/Application/Sources/Home/HomeMediaListTableViewCell.m index 5f4832a75..53e58b3a6 100755 --- a/Application/Sources/Home/HomeMediaListTableViewCell.m +++ b/Application/Sources/Home/HomeMediaListTableViewCell.m @@ -219,11 +219,14 @@ - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPa if (homeSection == HomeSectionTVLive) { ApplicationSettingSetSetLastSelectedTVLivestreamURN(media.URN); } - else if (homeSection == HomeSectionRadioLive) { + else { ApplicationSettingSetSetLastSelectedRadioLivestreamURN(media.URN); } - - [self.nearestViewController play_presentMediaPlayerWithMedia:media position:nil airPlaySuggestions:YES fromPushNotification:NO animated:YES completion:nil]; + [self.nearestViewController play_presentMediaPlayerWithMedia:media position:nil airPlaySuggestions:YES fromPushNotification:NO animated:YES completion:^(PlayerType playerType) { + if (homeSection == HomeSectionTVLive || homeSection == HomeSectionRadioLive) { + self.collectionView.contentOffset = CGPointMake(0.f, self.collectionView.contentOffset.y); + } + }]; } } diff --git a/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.m b/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.m index d5f7661e8..6ec8d1d67 100755 --- a/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.m +++ b/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.m @@ -7,6 +7,7 @@ #import "HomeLiveMediaCollectionViewCell.h" #import "AnalyticsConstants.h" +#import "ApplicationSettings.h" #import "ChannelService.h" #import "Layout.h" #import "NSBundle+PlaySRG.h" @@ -33,6 +34,7 @@ @interface HomeLiveMediaCollectionViewCell () @property (nonatomic, weak) IBOutlet UIImageView *placeholderImageView; @property (nonatomic, weak) IBOutlet UIImageView *logoImageView; +@property (nonatomic, weak) IBOutlet UILabel *recentLabel; @property (nonatomic, weak) IBOutlet UILabel *titleLabel; @property (nonatomic, weak) IBOutlet UILabel *subtitleLabel; @property (nonatomic, weak) IBOutlet UIImageView *thumbnailImageView; @@ -73,6 +75,12 @@ - (void)awakeFromNib self.thumbnailImageView.layer.cornerRadius = LayoutStandardViewCornerRadius; self.thumbnailImageView.layer.masksToBounds = YES; + self.recentLabel.layer.cornerRadius = LayoutStandardLabelCornerRadius; + self.recentLabel.layer.masksToBounds = YES; + self.recentLabel.backgroundColor = UIColor.play_redColor; + self.recentLabel.text = [NSString stringWithFormat:@" %@ ", NSLocalizedString(@"Recently played", @"Label on recently played livestreams").uppercaseString]; + self.recentLabel.hidden = YES; + self.durationLabel.backgroundColor = UIColor.play_blackDurationLabelBackgroundColor; self.blockingOverlayView.hidden = YES; @@ -207,6 +215,10 @@ - (void)reloadData self.titleLabel.font = [UIFont srg_mediumFontWithTextStyle:SRGAppearanceFontTextStyleBody]; self.durationLabel.font = [UIFont srg_mediumFontWithTextStyle:SRGAppearanceFontTextStyleCaption]; + self.recentLabel.font = [UIFont srg_mediumFontWithTextStyle:SRGAppearanceFontTextStyleCaption]; + self.recentLabel.hidden = ! [self.media.URN isEqualToString:ApplicationSettingLastSelectedTVLivestreamURN()] + && ! [self.media.URN isEqualToString:ApplicationSettingLastSelectedRadioLivestreamURN()]; + SRGBlockingReason blockingReason = [self.media blockingReasonAtDate:NSDate.date]; if (blockingReason == SRGBlockingReasonNone || blockingReason == SRGBlockingReasonStartDate) { self.blockingOverlayView.hidden = YES; diff --git a/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.xib b/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.xib index d799c4977..94354fd38 100755 --- a/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.xib +++ b/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.xib @@ -1,9 +1,9 @@ - + - + @@ -147,9 +147,22 @@ + + @@ -158,6 +171,7 @@ + @@ -188,6 +202,7 @@ + From 464db62264fc2a9a542a14a1687b4329104e919e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 24 Apr 2020 20:23:49 +0200 Subject: [PATCH 43/62] Take into account regional livestream selection --- .../Sources/Home/HomeMediaListTableViewCell.m | 4 ++-- .../Player/MediaPlayerViewController.m | 1 + .../Sources/Settings/ApplicationSettings.h | 4 ++-- .../Sources/Settings/ApplicationSettings.m | 24 +++++++++++++++++++ 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/Application/Sources/Home/HomeMediaListTableViewCell.m b/Application/Sources/Home/HomeMediaListTableViewCell.m index 53e58b3a6..71cd4b3b3 100755 --- a/Application/Sources/Home/HomeMediaListTableViewCell.m +++ b/Application/Sources/Home/HomeMediaListTableViewCell.m @@ -217,10 +217,10 @@ - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPa HomeSection homeSection = self.homeSectionInfo.homeSection; if (homeSection == HomeSectionTVLive) { - ApplicationSettingSetSetLastSelectedTVLivestreamURN(media.URN); + ApplicationSettingSetLastSelectedTVLivestreamURN(media.URN); } else { - ApplicationSettingSetSetLastSelectedRadioLivestreamURN(media.URN); + ApplicationSettingSetLastSelectedRadioLivestreamURN(media.URN); } [self.nearestViewController play_presentMediaPlayerWithMedia:media position:nil airPlaySuggestions:YES fromPushNotification:NO animated:YES completion:^(PlayerType playerType) { if (homeSection == HomeSectionTVLive || homeSection == HomeSectionRadioLive) { diff --git a/Application/Sources/Player/MediaPlayerViewController.m b/Application/Sources/Player/MediaPlayerViewController.m index dec2cb143..d24796c6a 100755 --- a/Application/Sources/Player/MediaPlayerViewController.m +++ b/Application/Sources/Player/MediaPlayerViewController.m @@ -1726,6 +1726,7 @@ - (IBAction)selectLivestreamMedia:(id)sender [self.livestreamMedias enumerateObjectsUsingBlock:^(SRGMedia * _Nonnull media, NSUInteger idx, BOOL * _Nonnull stop) { [alertController addAction:[UIAlertAction actionWithTitle:media.title style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + ApplicationSettingSetLastSelectedRadioLivestreamURN(media.URN); ApplicationSettingSetSelectedLivestreamURNForChannelUid(media.channel.uid, media.URN); // Use the playback state if playing diff --git a/Application/Sources/Settings/ApplicationSettings.h b/Application/Sources/Settings/ApplicationSettings.h index 84480ccb8..c61a14729 100755 --- a/Application/Sources/Settings/ApplicationSettings.h +++ b/Application/Sources/Settings/ApplicationSettings.h @@ -73,10 +73,10 @@ OBJC_EXPORT BOOL ApplicationSettingSubtitleAvailabilityDisplayed(void); OBJC_EXPORT BOOL ApplicationSettingAudioDescriptionAvailabilityDisplayed(void); OBJC_EXPORT NSString * _Nullable ApplicationSettingLastSelectedTVLivestreamURN(void); -OBJC_EXPORT void ApplicationSettingSetSetLastSelectedTVLivestreamURN(NSString * _Nullable mediaURN); +OBJC_EXPORT void ApplicationSettingSetLastSelectedTVLivestreamURN(NSString * _Nullable mediaURN); OBJC_EXPORT NSString * _Nullable ApplicationSettingLastSelectedRadioLivestreamURN(void); -OBJC_EXPORT void ApplicationSettingSetSetLastSelectedRadioLivestreamURN(NSString * _Nullable mediaURN); +OBJC_EXPORT void ApplicationSettingSetLastSelectedRadioLivestreamURN(NSString * _Nullable mediaURN); OBJC_EXPORT NSString * _Nullable ApplicationSettingSelectedLivestreamURNForChannelUid(NSString * _Nullable channelUid); OBJC_EXPORT void ApplicationSettingSetSelectedLivestreamURNForChannelUid(NSString * channelUid, NSString * _Nullable mediaURN); diff --git a/Application/Sources/Settings/ApplicationSettings.m b/Application/Sources/Settings/ApplicationSettings.m index 020324b4b..c2aa62012 100755 --- a/Application/Sources/Settings/ApplicationSettings.m +++ b/Application/Sources/Settings/ApplicationSettings.m @@ -209,6 +209,30 @@ BOOL ApplicationSettingAudioDescriptionAvailabilityDisplayed(void) return UIAccessibilityIsVoiceOverRunning() || [NSUserDefaults.standardUserDefaults boolForKey:PlaySRGSettingAudioDescriptionAvailabilityDisplayed]; } +NSString *ApplicationSettingLastSelectedTVLivestreamURN(void) +{ + return [NSUserDefaults.standardUserDefaults stringForKey:PlaySRGSettingSelectedTVLivestreamURN]; +} + +void ApplicationSettingSetLastSelectedTVLivestreamURN(NSString *mediaURN) +{ + NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults; + [userDefaults setObject:mediaURN forKey:PlaySRGSettingSelectedTVLivestreamURN]; + [userDefaults synchronize]; +} + +NSString *ApplicationSettingLastSelectedRadioLivestreamURN(void) +{ + return [NSUserDefaults.standardUserDefaults stringForKey:PlaySRGSettingSelectedRadioLivestreamURN]; +} + +void ApplicationSettingSetLastSelectedRadioLivestreamURN(NSString *mediaURN) +{ + NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults; + [userDefaults setObject:mediaURN forKey:PlaySRGSettingSelectedRadioLivestreamURN]; + [userDefaults synchronize]; +} + NSString *ApplicationSettingSelectedLivestreamURNForChannelUid(NSString *channelUid) { NSDictionary *selectedLivestreamURNForChannels = [NSUserDefaults.standardUserDefaults dictionaryForKey:PlaySRGSettingSelectedLivestreamURNForChannels]; From a6dbd904a41e57255c321922bd397c45d5a22545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Fri, 24 Apr 2020 21:10:51 +0200 Subject: [PATCH 44/62] Fix latest radio URN erased when playing another media --- Application/Sources/Home/HomeMediaListTableViewCell.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Application/Sources/Home/HomeMediaListTableViewCell.m b/Application/Sources/Home/HomeMediaListTableViewCell.m index 71cd4b3b3..6c4c11194 100755 --- a/Application/Sources/Home/HomeMediaListTableViewCell.m +++ b/Application/Sources/Home/HomeMediaListTableViewCell.m @@ -219,7 +219,7 @@ - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPa if (homeSection == HomeSectionTVLive) { ApplicationSettingSetLastSelectedTVLivestreamURN(media.URN); } - else { + else if (homeSection == HomeSectionRadioLive) { ApplicationSettingSetLastSelectedRadioLivestreamURN(media.URN); } [self.nearestViewController play_presentMediaPlayerWithMedia:media position:nil airPlaySuggestions:YES fromPushNotification:NO animated:YES completion:^(PlayerType playerType) { From 8c3f632c1be3b74532fa16238df9c91aa7f11a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Tue, 28 Apr 2020 16:03:37 +0200 Subject: [PATCH 45/62] Update label --- .../Resources/Apps/Play RTS/fr.lproj/Localizable.strings | 2 +- Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Application/Resources/Apps/Play RTS/fr.lproj/Localizable.strings b/Application/Resources/Apps/Play RTS/fr.lproj/Localizable.strings index 7edf7131c..435892481 100644 --- a/Application/Resources/Apps/Play RTS/fr.lproj/Localizable.strings +++ b/Application/Resources/Apps/Play RTS/fr.lproj/Localizable.strings @@ -353,7 +353,7 @@ /* Title displayed when no media is being played on the connected Google Cast receiver (name unknown) */ "Receiver is idle." = "Le récepteur est en attente."; -"Recently played" = "Joué récemment"; +"Recently played" = "Joué en dernier"; /* Hint displayed when no history is available */ "Recently played medias will be displayed here" = "Les contenus récemment consultés apparaîtront ici"; diff --git a/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.m b/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.m index 6ec8d1d67..1ed8b2566 100755 --- a/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.m +++ b/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.m @@ -78,7 +78,7 @@ - (void)awakeFromNib self.recentLabel.layer.cornerRadius = LayoutStandardLabelCornerRadius; self.recentLabel.layer.masksToBounds = YES; self.recentLabel.backgroundColor = UIColor.play_redColor; - self.recentLabel.text = [NSString stringWithFormat:@" %@ ", NSLocalizedString(@"Recently played", @"Label on recently played livestreams").uppercaseString]; + self.recentLabel.text = [NSString stringWithFormat:@" %@ ", NSLocalizedString(@"Last played", @"Label on recently played livestreams").uppercaseString]; self.recentLabel.hidden = YES; self.durationLabel.backgroundColor = UIColor.play_blackDurationLabelBackgroundColor; From 6edaf9f1cf9939a7f24c2926a88bce597a408ef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20De=CC=81fago?= Date: Thu, 30 Apr 2020 10:30:56 +0200 Subject: [PATCH 46/62] Display recent label with the same background as time labels --- Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.m | 2 +- .../Sources/UI/Views/HomeLiveMediaCollectionViewCell.xib | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.m b/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.m index 1ed8b2566..f00012a0f 100755 --- a/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.m +++ b/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.m @@ -77,7 +77,7 @@ - (void)awakeFromNib self.recentLabel.layer.cornerRadius = LayoutStandardLabelCornerRadius; self.recentLabel.layer.masksToBounds = YES; - self.recentLabel.backgroundColor = UIColor.play_redColor; + self.recentLabel.backgroundColor = UIColor.play_blackDurationLabelBackgroundColor; self.recentLabel.text = [NSString stringWithFormat:@" %@ ", NSLocalizedString(@"Last played", @"Label on recently played livestreams").uppercaseString]; self.recentLabel.hidden = YES; diff --git a/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.xib b/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.xib index 94354fd38..e10483cdd 100755 --- a/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.xib +++ b/Application/Sources/UI/Views/HomeLiveMediaCollectionViewCell.xib @@ -149,7 +149,7 @@