Skip to content
This repository was archived by the owner on Jan 17, 2019. It is now read-only.

Commit 711728d

Browse files
committed
Adding tvOS support
1 parent 185da1f commit 711728d

File tree

12 files changed

+1114
-6
lines changed

12 files changed

+1114
-6
lines changed

HubFramework.xcodeproj/project.pbxproj

Lines changed: 849 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "0820"
4+
version = "1.3">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES">
8+
<BuildActionEntries>
9+
<BuildActionEntry
10+
buildForTesting = "YES"
11+
buildForRunning = "YES"
12+
buildForProfiling = "YES"
13+
buildForArchiving = "YES"
14+
buildForAnalyzing = "YES">
15+
<BuildableReference
16+
BuildableIdentifier = "primary"
17+
BlueprintIdentifier = "344C5E3C1E09CF9A00597B61"
18+
BuildableName = "HubFramework.framework"
19+
BlueprintName = "HubFramework-tvOS"
20+
ReferencedContainer = "container:HubFramework.xcodeproj">
21+
</BuildableReference>
22+
</BuildActionEntry>
23+
</BuildActionEntries>
24+
</BuildAction>
25+
<TestAction
26+
buildConfiguration = "Debug"
27+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+
shouldUseLaunchSchemeArgsEnv = "YES">
30+
<Testables>
31+
<TestableReference
32+
skipped = "NO">
33+
<BuildableReference
34+
BuildableIdentifier = "primary"
35+
BlueprintIdentifier = "345AFDD31E10797B00807DCC"
36+
BuildableName = "HubFrameworkTests-tvOS.xctest"
37+
BlueprintName = "HubFrameworkTests-tvOS"
38+
ReferencedContainer = "container:HubFramework.xcodeproj">
39+
</BuildableReference>
40+
</TestableReference>
41+
</Testables>
42+
<MacroExpansion>
43+
<BuildableReference
44+
BuildableIdentifier = "primary"
45+
BlueprintIdentifier = "344C5E3C1E09CF9A00597B61"
46+
BuildableName = "HubFramework.framework"
47+
BlueprintName = "HubFramework-tvOS"
48+
ReferencedContainer = "container:HubFramework.xcodeproj">
49+
</BuildableReference>
50+
</MacroExpansion>
51+
<AdditionalOptions>
52+
</AdditionalOptions>
53+
</TestAction>
54+
<LaunchAction
55+
buildConfiguration = "Debug"
56+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
57+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
58+
launchStyle = "0"
59+
useCustomWorkingDirectory = "NO"
60+
ignoresPersistentStateOnLaunch = "NO"
61+
debugDocumentVersioning = "YES"
62+
debugServiceExtension = "internal"
63+
allowLocationSimulation = "YES">
64+
<MacroExpansion>
65+
<BuildableReference
66+
BuildableIdentifier = "primary"
67+
BlueprintIdentifier = "344C5E3C1E09CF9A00597B61"
68+
BuildableName = "HubFramework.framework"
69+
BlueprintName = "HubFramework-tvOS"
70+
ReferencedContainer = "container:HubFramework.xcodeproj">
71+
</BuildableReference>
72+
</MacroExpansion>
73+
<AdditionalOptions>
74+
</AdditionalOptions>
75+
</LaunchAction>
76+
<ProfileAction
77+
buildConfiguration = "Release"
78+
shouldUseLaunchSchemeArgsEnv = "YES"
79+
savedToolIdentifier = ""
80+
useCustomWorkingDirectory = "NO"
81+
debugDocumentVersioning = "YES">
82+
<MacroExpansion>
83+
<BuildableReference
84+
BuildableIdentifier = "primary"
85+
BlueprintIdentifier = "344C5E3C1E09CF9A00597B61"
86+
BuildableName = "HubFramework.framework"
87+
BlueprintName = "HubFramework-tvOS"
88+
ReferencedContainer = "container:HubFramework.xcodeproj">
89+
</BuildableReference>
90+
</MacroExpansion>
91+
</ProfileAction>
92+
<AnalyzeAction
93+
buildConfiguration = "Debug">
94+
</AnalyzeAction>
95+
<ArchiveAction
96+
buildConfiguration = "Release"
97+
revealArchiveInOrganizer = "YES">
98+
</ArchiveAction>
99+
</Scheme>

include/HubFramework/HUBComponent.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ NS_ASSUME_NONNULL_BEGIN
5353
*
5454
* `HUBComponentWithSelectionState`: For responding to highlight & selection events in a component.
5555
*
56+
* `HUBComponentWithFocusState`: For responding to focus events in a component (tvOS only).
57+
*
5658
* `HUBComponentContentOffsetObserver`: For components that react to the view's content offset.
5759
*
5860
* `HUBComponentViewObserver`: For components that observe their view for various events.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2016 Spotify AB.
3+
*
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
#import "HUBComponent.h"
23+
24+
/// Enum defining various focus states that a component can be in
25+
typedef NS_ENUM(NSUInteger, HUBComponentFocusState) {
26+
/// The component is not in focus
27+
HUBComponentFocusStateNone,
28+
/// The component is in focus, either programmatically or by the user
29+
HUBComponentFocusStateInFocus
30+
};
31+
32+
/**
33+
* Extended Hub component protocol that adds the ability to respond to focus events (tvOS only).
34+
*
35+
* Use this protocol if your component adjusts its appearance when the user focuses on it.
36+
*
37+
* For more information, see `HUBComponent` and `HUBComponentFocusState`.
38+
*/
39+
@protocol HUBComponentWithFocusState <HUBComponent>
40+
41+
/**
42+
* Update the components view for a certain focus state
43+
*
44+
* @param focusState The new focus state that the component's view should be updated for
45+
*
46+
* The Hub Framework automatically sends this message to a component when the user focuses on it.
47+
*/
48+
- (void)updateViewForFocusState:(HUBComponentFocusState)focusState NS_SWIFT_NAME(updateViewForFocusState(_:));
49+
50+
@end

include/HubFramework/HubFramework.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
#import "HUBComponentWithScrolling.h"
7070
#import "HUBComponentWithImageHandling.h"
7171
#import "HUBComponentWithRestorableUIState.h"
72+
#import "HUBComponentWithFocusState.h"
7273
#import "HUBComponentWithSelectionState.h"
7374
#import "HUBComponentContentOffsetObserver.h"
7475
#import "HUBComponentViewObserver.h"

sources/HUBComponentGestureRecognizer.m

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,25 @@ - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
7171
self.state = UIGestureRecognizerStateCancelled;
7272
}
7373

74+
#if TARGET_OS_TV
75+
76+
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
77+
{
78+
self.state = UIGestureRecognizerStateBegan;
79+
}
80+
81+
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
82+
{
83+
self.state = UIGestureRecognizerStateEnded;
84+
}
85+
86+
- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
87+
{
88+
self.state = UIGestureRecognizerStateCancelled;
89+
}
90+
91+
#endif
92+
7493
@end
7594

7695
NS_ASSUME_NONNULL_END

sources/HUBComponentWrapper.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#import "HUBComponentActionObserver.h"
2626
#import "HUBComponentWithRestorableUIState.h"
2727
#import "HUBComponentWithSelectionState.h"
28+
#import "HUBComponentWithFocusState.h"
2829
#import "HUBComponentWithScrolling.h"
2930
#import "HUBHeaderMacros.h"
3031

@@ -137,6 +138,7 @@ willUpdateSelectionState:(HUBComponentSelectionState)selectionState;
137138
HUBComponentContentOffsetObserver,
138139
HUBComponentActionObserver,
139140
HUBComponentWithSelectionState,
141+
HUBComponentWithFocusState,
140142
HUBComponentWithScrolling
141143
>
142144

sources/HUBComponentWrapper.m

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,13 @@ - (void)updateViewForSelectionState:(HUBComponentSelectionState)selectionState n
573573
}
574574
}
575575

576+
- (void)updateViewForFocusState:(HUBComponentFocusState)focusState
577+
{
578+
if ([self.component conformsToProtocol:@protocol(HUBComponentWithFocusState)]) {
579+
[(id<HUBComponentWithFocusState>)self.component updateViewForFocusState:focusState];
580+
}
581+
}
582+
576583
- (CGRect)calculateViewFrameInWindow
577584
{
578585
UIView * const view = HUBComponentLoadViewIfNeeded(self);

sources/HUBUtilities.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ static inline NSDictionary<NSString *, id> * _Nullable HUBMergeDictionaries(NSDi
154154
*/
155155
static inline NSArray<NSString *> *HUBNavigationItemPropertyNames()
156156
{
157+
#if TARGET_OS_TV
158+
return @[
159+
HUBKeyPath((UINavigationItem *)nil, title),
160+
HUBKeyPath((UINavigationItem *)nil, titleView),
161+
HUBKeyPath((UINavigationItem *)nil, leftBarButtonItems),
162+
HUBKeyPath((UINavigationItem *)nil, rightBarButtonItems),
163+
];
164+
#else
157165
return @[
158166
HUBKeyPath((UINavigationItem *)nil, title),
159167
HUBKeyPath((UINavigationItem *)nil, titleView),
@@ -164,6 +172,7 @@ static inline NSArray<NSString *> *HUBNavigationItemPropertyNames()
164172
HUBKeyPath((UINavigationItem *)nil, rightBarButtonItems),
165173
HUBKeyPath((UINavigationItem *)nil, leftItemsSupplementBackButton)
166174
];
175+
#endif
167176
}
168177

169178
/**
@@ -199,20 +208,22 @@ static inline BOOL HUBNavigationItemEqualToNavigationItem(UINavigationItem *navi
199208
*/
200209
static inline UINavigationItem *HUBCopyNavigationItemProperties(UINavigationItem *navigationItemA, UINavigationItem * _Nullable navigationItemB)
201210
{
211+
#if !TARGET_OS_TV
202212
NSSet<NSString *> * const boolPropertyNames = [NSSet setWithObjects:HUBKeyPath(navigationItemA, hidesBackButton),
203213
HUBKeyPath(navigationItemA, leftItemsSupplementBackButton),
204214
nil];
205-
215+
#endif
206216
for (NSString * const propertyName in HUBNavigationItemPropertyNames()) {
207217
id const value = [navigationItemB valueForKey:propertyName];
208-
218+
219+
#if !TARGET_OS_TV
209220
if (value == nil) {
210221
if ([boolPropertyNames containsObject:propertyName]) {
211222
navigationItemA.hidesBackButton = NO;
212223
continue;
213224
}
214225
}
215-
226+
#endif
216227
[navigationItemA setValue:value forKey:propertyName];
217228
}
218229

sources/HUBViewController.m

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ - (void)loadView
177177
- (void)viewWillAppear:(BOOL)animated
178178
{
179179
[super viewWillAppear:animated];
180+
181+
#if !TARGET_OS_TV
180182

181183
NSNotificationCenter * const notificationCenter = [NSNotificationCenter defaultCenter];
182184

@@ -189,6 +191,8 @@ - (void)viewWillAppear:(BOOL)animated
189191
selector:@selector(handleKeyboardWillHideNotification:)
190192
name:UIKeyboardWillHideNotification
191193
object:nil];
194+
195+
#endif
192196

193197
if (self.viewModel == nil) {
194198
self.viewModel = self.viewModelLoader.initialViewModel;
@@ -214,11 +218,15 @@ - (void)viewDidAppear:(BOOL)animated
214218
- (void)viewWillDisappear:(BOOL)animated
215219
{
216220
[super viewWillDisappear:animated];
217-
221+
222+
#if !TARGET_OS_TV
223+
218224
NSNotificationCenter * const notificationCenter = [NSNotificationCenter defaultCenter];
219225
[notificationCenter removeObserver:self name:UIKeyboardWillShowNotification object:nil];
220226
[notificationCenter removeObserver:self name:UIKeyboardWillHideNotification object:nil];
221-
227+
228+
#endif
229+
222230
self.viewHasBeenLaidOut = NO;
223231
}
224232

@@ -774,6 +782,15 @@ - (BOOL)collectionViewShouldBeginScrolling:(HUBCollectionView *)collectionView
774782
return [delegate viewControllerShouldStartScrolling:self];
775783
}
776784

785+
#if TARGET_OS_TV
786+
- (BOOL)collectionView:(UICollectionView *)collectionView canFocusItemAtIndexPath:(NSIndexPath *)indexPath
787+
{
788+
HUBComponentCollectionViewCell *cell = (HUBComponentCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
789+
HUBComponentWrapper * const wrapper = [self componentWrapperFromCell:cell];
790+
return wrapper.visibleChildren.count == 0;
791+
}
792+
#endif
793+
777794
#pragma mark - UIScrollViewDelegate
778795

779796
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
@@ -858,6 +875,7 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecogni
858875
}
859876

860877
#pragma mark - Notification handling
878+
#if !TARGET_OS_TV
861879

862880
- (void)handleKeyboardWillShowNotification:(NSNotification *)notification
863881
{
@@ -871,6 +889,7 @@ - (void)handleKeyboardWillHideNotification:(NSNotification *)notification
871889
self.visibleKeyboardHeight = 0;
872890
[self updateOverlayComponentCenterPointsWithKeyboardNotification:notification];
873891
}
892+
#endif
874893

875894
#pragma mark - Private utilities
876895

@@ -984,9 +1003,13 @@ - (CGFloat)calculateTopContentInset
9841003
if (self.headerComponentWrapper != nil) {
9851004
return 0;
9861005
}
987-
1006+
#if !TARGET_OS_TV
9881007
CGFloat const statusBarWidth = CGRectGetWidth([UIApplication sharedApplication].statusBarFrame);
9891008
CGFloat const statusBarHeight = CGRectGetHeight([UIApplication sharedApplication].statusBarFrame);
1009+
#else
1010+
CGFloat const statusBarWidth = 0.0;
1011+
CGFloat const statusBarHeight = 0.0;
1012+
#endif
9901013
CGFloat const navigationBarWidth = CGRectGetWidth(self.navigationController.navigationBar.frame);
9911014
CGFloat const navigationBarHeight = CGRectGetHeight(self.navigationController.navigationBar.frame);
9921015
CGFloat const topBarHeight = MIN(statusBarWidth, statusBarHeight) + MIN(navigationBarWidth, navigationBarHeight);
@@ -1066,6 +1089,7 @@ - (CGPoint)overlayComponentCenterPoint
10661089
proposedCenterPoint:proposedCenterPoint];
10671090
}
10681091

1092+
#if !TARGET_OS_TV
10691093
- (void)updateOverlayComponentCenterPointsWithKeyboardNotification:(NSNotification *)notification
10701094
{
10711095
NSTimeInterval const animationDuration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
@@ -1081,6 +1105,7 @@ - (void)updateOverlayComponentCenterPointsWithKeyboardNotification:(NSNotificati
10811105

10821106
[UIView commitAnimations];
10831107
}
1108+
#endif
10841109

10851110
- (HUBComponentWrapper *)configureHeaderOrOverlayComponentWrapperWithModel:(id<HUBComponentModel>)componentModel
10861111
previousComponentWrapper:(nullable HUBComponentWrapper *)previousComponentWrapper
@@ -1536,6 +1561,20 @@ - (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
15361561
[self.collectionView setContentOffset:contentOffset animated:animated];
15371562
}
15381563

1564+
#pragma mark - Focus engine
1565+
#if TARGET_OS_TV
1566+
1567+
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator {
1568+
HUBComponentCollectionViewCell *previousItem = (HUBComponentCollectionViewCell *)context.previouslyFocusedView;
1569+
HUBComponentWrapper * const previousWrapper = [self componentWrapperFromCell:previousItem];
1570+
[previousWrapper updateViewForFocusState:HUBComponentFocusStateNone];
1571+
HUBComponentCollectionViewCell *nextItem = (HUBComponentCollectionViewCell *)context.nextFocusedView;
1572+
HUBComponentWrapper * const nextWrapper = [self componentWrapperFromCell:nextItem];
1573+
[nextWrapper updateViewForFocusState:HUBComponentFocusStateInFocus];
1574+
}
1575+
1576+
#endif
1577+
15391578
@end
15401579

15411580
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)