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

Commit

Permalink
Adding tvOS support
Browse files Browse the repository at this point in the history
  • Loading branch information
marmelroy committed Dec 27, 2016
1 parent 185da1f commit 711728d
Show file tree
Hide file tree
Showing 12 changed files with 1,114 additions and 6 deletions.
849 changes: 849 additions & 0 deletions HubFramework.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "344C5E3C1E09CF9A00597B61"
BuildableName = "HubFramework.framework"
BlueprintName = "HubFramework-tvOS"
ReferencedContainer = "container:HubFramework.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "345AFDD31E10797B00807DCC"
BuildableName = "HubFrameworkTests-tvOS.xctest"
BlueprintName = "HubFrameworkTests-tvOS"
ReferencedContainer = "container:HubFramework.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "344C5E3C1E09CF9A00597B61"
BuildableName = "HubFramework.framework"
BlueprintName = "HubFramework-tvOS"
ReferencedContainer = "container:HubFramework.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "344C5E3C1E09CF9A00597B61"
BuildableName = "HubFramework.framework"
BlueprintName = "HubFramework-tvOS"
ReferencedContainer = "container:HubFramework.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "344C5E3C1E09CF9A00597B61"
BuildableName = "HubFramework.framework"
BlueprintName = "HubFramework-tvOS"
ReferencedContainer = "container:HubFramework.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
2 changes: 2 additions & 0 deletions include/HubFramework/HUBComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ NS_ASSUME_NONNULL_BEGIN
*
* `HUBComponentWithSelectionState`: For responding to highlight & selection events in a component.
*
* `HUBComponentWithFocusState`: For responding to focus events in a component (tvOS only).
*
* `HUBComponentContentOffsetObserver`: For components that react to the view's content offset.
*
* `HUBComponentViewObserver`: For components that observe their view for various events.
Expand Down
50 changes: 50 additions & 0 deletions include/HubFramework/HUBComponentWithFocusState.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2016 Spotify AB.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

#import "HUBComponent.h"

/// Enum defining various focus states that a component can be in
typedef NS_ENUM(NSUInteger, HUBComponentFocusState) {
/// The component is not in focus
HUBComponentFocusStateNone,
/// The component is in focus, either programmatically or by the user
HUBComponentFocusStateInFocus
};

/**
* Extended Hub component protocol that adds the ability to respond to focus events (tvOS only).
*
* Use this protocol if your component adjusts its appearance when the user focuses on it.
*
* For more information, see `HUBComponent` and `HUBComponentFocusState`.
*/
@protocol HUBComponentWithFocusState <HUBComponent>

/**
* Update the components view for a certain focus state
*
* @param focusState The new focus state that the component's view should be updated for
*
* The Hub Framework automatically sends this message to a component when the user focuses on it.
*/
- (void)updateViewForFocusState:(HUBComponentFocusState)focusState NS_SWIFT_NAME(updateViewForFocusState(_:));

@end
1 change: 1 addition & 0 deletions include/HubFramework/HubFramework.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
#import "HUBComponentWithScrolling.h"
#import "HUBComponentWithImageHandling.h"
#import "HUBComponentWithRestorableUIState.h"
#import "HUBComponentWithFocusState.h"
#import "HUBComponentWithSelectionState.h"
#import "HUBComponentContentOffsetObserver.h"
#import "HUBComponentViewObserver.h"
Expand Down
19 changes: 19 additions & 0 deletions sources/HUBComponentGestureRecognizer.m
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,25 @@ - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
self.state = UIGestureRecognizerStateCancelled;
}

#if TARGET_OS_TV

- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
{
self.state = UIGestureRecognizerStateBegan;
}

- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
{
self.state = UIGestureRecognizerStateEnded;
}

- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
{
self.state = UIGestureRecognizerStateCancelled;
}

#endif

@end

NS_ASSUME_NONNULL_END
2 changes: 2 additions & 0 deletions sources/HUBComponentWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#import "HUBComponentActionObserver.h"
#import "HUBComponentWithRestorableUIState.h"
#import "HUBComponentWithSelectionState.h"
#import "HUBComponentWithFocusState.h"
#import "HUBComponentWithScrolling.h"
#import "HUBHeaderMacros.h"

Expand Down Expand Up @@ -137,6 +138,7 @@ willUpdateSelectionState:(HUBComponentSelectionState)selectionState;
HUBComponentContentOffsetObserver,
HUBComponentActionObserver,
HUBComponentWithSelectionState,
HUBComponentWithFocusState,
HUBComponentWithScrolling
>

Expand Down
7 changes: 7 additions & 0 deletions sources/HUBComponentWrapper.m
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,13 @@ - (void)updateViewForSelectionState:(HUBComponentSelectionState)selectionState n
}
}

- (void)updateViewForFocusState:(HUBComponentFocusState)focusState
{
if ([self.component conformsToProtocol:@protocol(HUBComponentWithFocusState)]) {
[(id<HUBComponentWithFocusState>)self.component updateViewForFocusState:focusState];
}
}

- (CGRect)calculateViewFrameInWindow
{
UIView * const view = HUBComponentLoadViewIfNeeded(self);
Expand Down
17 changes: 14 additions & 3 deletions sources/HUBUtilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@ static inline NSDictionary<NSString *, id> * _Nullable HUBMergeDictionaries(NSDi
*/
static inline NSArray<NSString *> *HUBNavigationItemPropertyNames()
{
#if TARGET_OS_TV
return @[
HUBKeyPath((UINavigationItem *)nil, title),
HUBKeyPath((UINavigationItem *)nil, titleView),
HUBKeyPath((UINavigationItem *)nil, leftBarButtonItems),
HUBKeyPath((UINavigationItem *)nil, rightBarButtonItems),
];
#else
return @[
HUBKeyPath((UINavigationItem *)nil, title),
HUBKeyPath((UINavigationItem *)nil, titleView),
Expand All @@ -164,6 +172,7 @@ static inline NSArray<NSString *> *HUBNavigationItemPropertyNames()
HUBKeyPath((UINavigationItem *)nil, rightBarButtonItems),
HUBKeyPath((UINavigationItem *)nil, leftItemsSupplementBackButton)
];
#endif
}

/**
Expand Down Expand Up @@ -199,20 +208,22 @@ static inline BOOL HUBNavigationItemEqualToNavigationItem(UINavigationItem *navi
*/
static inline UINavigationItem *HUBCopyNavigationItemProperties(UINavigationItem *navigationItemA, UINavigationItem * _Nullable navigationItemB)
{
#if !TARGET_OS_TV
NSSet<NSString *> * const boolPropertyNames = [NSSet setWithObjects:HUBKeyPath(navigationItemA, hidesBackButton),
HUBKeyPath(navigationItemA, leftItemsSupplementBackButton),
nil];

#endif
for (NSString * const propertyName in HUBNavigationItemPropertyNames()) {
id const value = [navigationItemB valueForKey:propertyName];


#if !TARGET_OS_TV
if (value == nil) {
if ([boolPropertyNames containsObject:propertyName]) {
navigationItemA.hidesBackButton = NO;
continue;
}
}

#endif
[navigationItemA setValue:value forKey:propertyName];
}

Expand Down
45 changes: 42 additions & 3 deletions sources/HUBViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ - (void)loadView
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];

#if !TARGET_OS_TV

NSNotificationCenter * const notificationCenter = [NSNotificationCenter defaultCenter];

Expand All @@ -189,6 +191,8 @@ - (void)viewWillAppear:(BOOL)animated
selector:@selector(handleKeyboardWillHideNotification:)
name:UIKeyboardWillHideNotification
object:nil];

#endif

if (self.viewModel == nil) {
self.viewModel = self.viewModelLoader.initialViewModel;
Expand All @@ -214,11 +218,15 @@ - (void)viewDidAppear:(BOOL)animated
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];


#if !TARGET_OS_TV

NSNotificationCenter * const notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[notificationCenter removeObserver:self name:UIKeyboardWillHideNotification object:nil];


#endif

self.viewHasBeenLaidOut = NO;
}

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

#if TARGET_OS_TV
- (BOOL)collectionView:(UICollectionView *)collectionView canFocusItemAtIndexPath:(NSIndexPath *)indexPath
{
HUBComponentCollectionViewCell *cell = (HUBComponentCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
HUBComponentWrapper * const wrapper = [self componentWrapperFromCell:cell];
return wrapper.visibleChildren.count == 0;
}
#endif

#pragma mark - UIScrollViewDelegate

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
Expand Down Expand Up @@ -858,6 +875,7 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecogni
}

#pragma mark - Notification handling
#if !TARGET_OS_TV

- (void)handleKeyboardWillShowNotification:(NSNotification *)notification
{
Expand All @@ -871,6 +889,7 @@ - (void)handleKeyboardWillHideNotification:(NSNotification *)notification
self.visibleKeyboardHeight = 0;
[self updateOverlayComponentCenterPointsWithKeyboardNotification:notification];
}
#endif

#pragma mark - Private utilities

Expand Down Expand Up @@ -984,9 +1003,13 @@ - (CGFloat)calculateTopContentInset
if (self.headerComponentWrapper != nil) {
return 0;
}

#if !TARGET_OS_TV
CGFloat const statusBarWidth = CGRectGetWidth([UIApplication sharedApplication].statusBarFrame);
CGFloat const statusBarHeight = CGRectGetHeight([UIApplication sharedApplication].statusBarFrame);
#else
CGFloat const statusBarWidth = 0.0;
CGFloat const statusBarHeight = 0.0;
#endif
CGFloat const navigationBarWidth = CGRectGetWidth(self.navigationController.navigationBar.frame);
CGFloat const navigationBarHeight = CGRectGetHeight(self.navigationController.navigationBar.frame);
CGFloat const topBarHeight = MIN(statusBarWidth, statusBarHeight) + MIN(navigationBarWidth, navigationBarHeight);
Expand Down Expand Up @@ -1066,6 +1089,7 @@ - (CGPoint)overlayComponentCenterPoint
proposedCenterPoint:proposedCenterPoint];
}

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

[UIView commitAnimations];
}
#endif

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

#pragma mark - Focus engine
#if TARGET_OS_TV

- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator {
HUBComponentCollectionViewCell *previousItem = (HUBComponentCollectionViewCell *)context.previouslyFocusedView;
HUBComponentWrapper * const previousWrapper = [self componentWrapperFromCell:previousItem];
[previousWrapper updateViewForFocusState:HUBComponentFocusStateNone];
HUBComponentCollectionViewCell *nextItem = (HUBComponentCollectionViewCell *)context.nextFocusedView;
HUBComponentWrapper * const nextWrapper = [self componentWrapperFromCell:nextItem];
[nextWrapper updateViewForFocusState:HUBComponentFocusStateInFocus];
}

#endif

@end

NS_ASSUME_NONNULL_END
Loading

0 comments on commit 711728d

Please sign in to comment.