Skip to content

Commit

Permalink
Merge pull request akiroom#15 from akiroom/features/free-stretchable-…
Browse files Browse the repository at this point in the history
…view

make AXStretchableSubViewControllerViewSource protocol
  • Loading branch information
Hiroki Akiyama committed Jul 31, 2014
2 parents a7f4680 + 7ca5971 commit e311f1e
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 83 deletions.
2 changes: 1 addition & 1 deletion AXStretchableHeaderTabViewController.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "AXStretchableHeaderTabViewController"
s.version = "0.1.8"
s.version = "0.1.9"
s.summary = "Stretchable header view + Horizontal swipable tab view."
s.description = <<-DESC
Stretchable header view + Horizontal swipable tab view
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# AXStretchableHeaderTabViewController CHANGELOG

## 0.1.9

Feature: AXStretchableSubViewControllerViewSource protocol is available to set scroll view.

## 0.1.8

Feature: No swipable header is default. Look at "No horizontal swipe header" example.
Expand Down
7 changes: 7 additions & 0 deletions Classes/AXStretchableHeaderTabViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
#import "AXStretchableHeaderView.h"
#import "AXTabBar.h"

@class AXStretchableHeaderTabViewController;

@protocol AXStretchableSubViewControllerViewSource <NSObject>
@optional
- (UIScrollView *)stretchableSubViewInSubViewController:(id)subViewController;
@end

@interface AXStretchableHeaderTabViewController : UIViewController <UIScrollViewDelegate, AXTabBarDelegate>
@property (nonatomic) NSUInteger selectedIndex;
@property (readwrite, nonatomic) UIViewController *selectedViewController;
Expand Down
128 changes: 47 additions & 81 deletions Classes/AXStretchableHeaderTabViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ - (void)viewDidLoad
- (void)dealloc
{
[_viewControllers enumerateObjectsUsingBlock:^(UIViewController *viewController, NSUInteger idx, BOOL *stop) {
[viewController.view removeObserver:self forKeyPath:@"contentOffset"];
UIScrollView *scrollView = [self scrollViewWithSubViewController:viewController];
if (scrollView) {
[scrollView removeObserver:self forKeyPath:@"contentOffset"];
}
[viewController removeFromParentViewController];
}];
}
Expand Down Expand Up @@ -102,7 +105,10 @@ - (void)setViewControllers:(NSArray *)viewControllers
// Remove views in old view controllers
[_viewControllers enumerateObjectsUsingBlock:^(UIViewController *viewController, NSUInteger idx, BOOL *stop) {
[viewController.view removeFromSuperview];
[viewController.view removeObserver:self forKeyPath:@"contentOffset"];
UIScrollView *scrollView = [self scrollViewWithSubViewController:viewController];
if (scrollView) {
[scrollView removeObserver:self forKeyPath:@"contentOffset"];
}
[viewController removeFromParentViewController];
}];

Expand All @@ -113,7 +119,10 @@ - (void)setViewControllers:(NSArray *)viewControllers
NSMutableArray *tabItems = [NSMutableArray array];
[_viewControllers enumerateObjectsUsingBlock:^(UIViewController *viewController, NSUInteger idx, BOOL *stop) {
[_containerView addSubview:viewController.view];
[viewController.view addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
UIScrollView *scrollView = [self scrollViewWithSubViewController:viewController];
if (scrollView) {
[scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
}
[self addChildViewController:viewController];
[tabItems addObject:viewController.tabBarItem];
}];
Expand All @@ -131,10 +140,12 @@ - (void)setViewControllers:(NSArray *)viewControllers

- (void)layoutHeaderViewAndTabBar
{
UIViewController *selectedViewController = self.selectedViewController;

// Get selected scroll view.
UIScrollView *scrollView = (id)[self selectedViewController].view;
UIScrollView *scrollView = [self scrollViewWithSubViewController:selectedViewController];

if ([scrollView isKindOfClass:[UIScrollView class]]) {
if (scrollView) {
// Set header view frame
CGFloat headerViewHeight = _headerView.maximumOfHeight - (scrollView.contentOffset.y + scrollView.contentInset.top);
headerViewHeight = MAX(headerViewHeight, _headerView.minimumOfHeight);
Expand Down Expand Up @@ -183,17 +194,14 @@ - (void)layoutViewControllers
UIEdgeInsets contentInsets = UIEdgeInsetsMake(headerOffset + CGRectGetHeight(_tabBar.bounds), 0.0, _containerView.contentInset.top, 0.0);

// Resize sub view controllers
[_viewControllers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[UIViewController class]]) {
UIViewController *viewController = obj;
CGRect newFrame = (CGRect){size.width * idx, 0.0, size};
if ([viewController.view isKindOfClass:[UIScrollView class]]) {
UIScrollView *scrollView = (id)viewController.view;
[scrollView setFrame:newFrame];
[scrollView setContentInset:contentInsets];
} else {
[viewController.view setFrame:UIEdgeInsetsInsetRect(newFrame, contentInsets)];
}
[_viewControllers enumerateObjectsUsingBlock:^(UIViewController *viewController, NSUInteger idx, BOOL *stop) {
CGRect newFrame = (CGRect){size.width * idx, 0.0, size};
UIScrollView *scrollView = [self scrollViewWithSubViewController:viewController];
if (scrollView) {
[viewController.view setFrame:newFrame];
[scrollView setContentInset:contentInsets];
} else {
[viewController.view setFrame:UIEdgeInsetsInsetRect(newFrame, contentInsets)];
}
}];
[_containerView setContentSize:(CGSize){size.width * _viewControllers.count, 0.0}];
Expand All @@ -202,12 +210,11 @@ - (void)layoutViewControllers
- (void)layoutSubViewControllerToSelectedViewController
{
UIViewController *selectedViewController = [self selectedViewController];
if ([selectedViewController.view isKindOfClass:[UIScrollView class]] == NO) {
// Define selected scroll view
UIScrollView *selectedScrollView = [self scrollViewWithSubViewController:selectedViewController];
if (!selectedScrollView) {
return;
}

// Define selected scroll view
UIScrollView *selectedScrollView = (id)selectedViewController.view;

// Define relative y calculator
CGFloat (^calcRelativeY)(CGFloat contentOffsetY, CGFloat contentInsetTop) = ^CGFloat(CGFloat contentOffsetY, CGFloat contentInsetTop) {
Expand All @@ -220,11 +227,10 @@ - (void)layoutSubViewControllerToSelectedViewController
return;
}

UIView *targetView = viewController.view;
if ([targetView isKindOfClass:[UIScrollView class]]) {
UIScrollView *targetScrollView = [self scrollViewWithSubViewController:viewController];
if ([targetScrollView isKindOfClass:[UIScrollView class]]) {
// Scroll view
// -> Adjust offset
UIScrollView *targetScrollView = (id)targetView;
CGFloat relativePositionY = calcRelativeY(selectedScrollView.contentOffset.y, selectedScrollView.contentInset.top);//headerViewHeight - _headerView.minimumOfHeight;
if (relativePositionY > 0) {
// The header view's height is higher than minimum height.
Expand All @@ -246,9 +252,9 @@ - (void)layoutSubViewControllerToSelectedViewController
// Not scroll view
// -> Adjust frame to area at the bottom of tab bar.
CGFloat y = CGRectGetMaxY(_tabBar.frame) - _containerView.contentInset.top;
[targetView setFrame:(CGRect){
CGRectGetMinX(targetView.frame), y,
CGRectGetMinX(targetView.frame), CGRectGetHeight(_containerView.frame) - y
[targetScrollView setFrame:(CGRect){
CGRectGetMinX(targetScrollView.frame), y,
CGRectGetMinX(targetScrollView.frame), CGRectGetHeight(_containerView.frame) - y
}];
}
}];
Expand All @@ -260,8 +266,8 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
{
UIViewController *selectedViewController = [self selectedViewController];
if ([keyPath isEqualToString:@"contentOffset"]) {
if ([selectedViewController view] != object &&
[[selectedViewController view] isKindOfClass:[UIScrollView class]] == NO) {
UIScrollView *scrollView = [self scrollViewWithSubViewController:selectedViewController];
if (scrollView != object) {
return;
}
[self layoutHeaderViewAndTabBar];
Expand All @@ -273,59 +279,6 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[self layoutSubViewControllerToSelectedViewController];
return;


UIViewController *selectedViewController = [self selectedViewController];
if ([selectedViewController.view isKindOfClass:[UIScrollView class]] == NO) {
return;
}
// Define selected scroll view
UIScrollView *selectedScrollView = (id)selectedViewController.view;

// Define relative y calculator
CGFloat (^calcRelativeY)(CGFloat contentOffsetY, CGFloat contentInsetTop) = ^CGFloat(CGFloat contentOffsetY, CGFloat contentInsetTop) {
return _headerView.maximumOfHeight - _headerView.minimumOfHeight - (contentOffsetY + contentInsetTop);
};

// Adjustment offset or frame for sub views.
[_viewControllers enumerateObjectsUsingBlock:^(UIViewController *viewController, NSUInteger idx, BOOL *stop) {
if (selectedViewController == viewController) {
return;
}

UIView *targetView = viewController.view;
if ([targetView isKindOfClass:[UIScrollView class]]) {
// Scroll view
// -> Adjust offset
UIScrollView *targetScrollView = (id)targetView;
CGFloat relativePositionY = calcRelativeY(selectedScrollView.contentOffset.y, selectedScrollView.contentInset.top);//headerViewHeight - _headerView.minimumOfHeight;
if (relativePositionY > 0) {
// The header view's height is higher than minimum height.
// -> Adjust same offset.
[targetScrollView setContentOffset:selectedScrollView.contentOffset];

} else {
// The header view height is lower than minimum height.
// -> Adjust top of scrollview, If target header view's height is higher than minimum height.
CGFloat targetRelativePositionY = calcRelativeY(targetScrollView.contentOffset.y, targetScrollView.contentInset.top);
if (targetRelativePositionY > 0) {
targetScrollView.contentOffset = (CGPoint){
targetScrollView.contentOffset.x,
-(CGRectGetMaxY(_tabBar.frame) - _containerView.contentInset.top)
};
}
}
} else {
// Not scroll view
// -> Adjust frame to area at the bottom of tab bar.
CGFloat y = CGRectGetMaxY(_tabBar.frame) - _containerView.contentInset.top;
[targetView setFrame:(CGRect){
CGRectGetMinX(targetView.frame), y,
CGRectGetMinX(targetView.frame), CGRectGetHeight(_containerView.frame) - y
}];
}
}];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
Expand All @@ -352,4 +305,17 @@ - (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
[_containerView setContentOffset:(CGPoint){_selectedIndex * CGRectGetWidth(_containerView.bounds), _containerView.contentOffset.y} animated:YES];
}

#pragma mark - Private Method

- (UIScrollView *)scrollViewWithSubViewController:(UIViewController *)viewController
{
if ([viewController respondsToSelector:@selector(stretchableSubViewInSubViewController:)]) {
return [(id<AXStretchableSubViewControllerViewSource>)viewController stretchableSubViewInSubViewController:viewController];
} else if ([viewController.view isKindOfClass:[UIScrollView class]]) {
return (id)viewController.view;
} else {
return nil;
}
}

@end
18 changes: 18 additions & 0 deletions Example/StretchableHeaderTabViewExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
75101E141935F7C500AE76E8 /* AXSampleNoBounceTabViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 75101E131935F7C500AE76E8 /* AXSampleNoBounceTabViewController.m */; };
75101E5E1936CE0F00AE76E8 /* AXSampleNoHeaderTabViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 75101E5D1936CE0F00AE76E8 /* AXSampleNoHeaderTabViewController.m */; };
75101E6E19370AC100AE76E8 /* AXEmptyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 75101E6D19370AC100AE76E8 /* AXEmptyViewController.m */; };
753E4CE6196148420051BFE2 /* Reveal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 753E4CE5196148420051BFE2 /* Reveal.framework */; };
7548149F193234F400CFD342 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7548149E193234F400CFD342 /* Foundation.framework */; };
754814A1193234F400CFD342 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 754814A0193234F400CFD342 /* CoreGraphics.framework */; };
754814A3193234F400CFD342 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 754814A2193234F400CFD342 /* UIKit.framework */; };
Expand All @@ -34,6 +35,7 @@
7584B66A194A875F00AEE4DF /* AXSampleNoSwipableTabViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7584B669194A875F00AEE4DF /* AXSampleNoSwipableTabViewController.m */; };
759D088E1935BEEB00E930BD /* AXSampleSwipableHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 759D088D1935BEEB00E930BD /* AXSampleSwipableHeaderView.m */; };
75A68DEB1935CBFD00C753FF /* AXSampleSwipableHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 75A68DEA1935CBFD00C753FF /* AXSampleSwipableHeaderView.xib */; };
75CA85621989DB34006EC828 /* AXSub4ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 75CA85611989DB34006EC828 /* AXSub4ViewController.m */; };
7777FECBD35D45B6935E3121 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 77E2F7A94B9542CEA1F1CC2F /* libPods.a */; };
/* End PBXBuildFile section */

Expand All @@ -54,6 +56,7 @@
75101E5D1936CE0F00AE76E8 /* AXSampleNoHeaderTabViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AXSampleNoHeaderTabViewController.m; sourceTree = "<group>"; };
75101E6C19370AC100AE76E8 /* AXEmptyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AXEmptyViewController.h; sourceTree = "<group>"; };
75101E6D19370AC100AE76E8 /* AXEmptyViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AXEmptyViewController.m; sourceTree = "<group>"; };
753E4CE5196148420051BFE2 /* Reveal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Reveal.framework; path = "../../../../../Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/Reveal.framework"; sourceTree = "<group>"; };
7548149B193234F400CFD342 /* StretchableHeaderTabViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StretchableHeaderTabViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
7548149E193234F400CFD342 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
754814A0193234F400CFD342 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -91,6 +94,8 @@
759D088C1935BEEB00E930BD /* AXSampleSwipableHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AXSampleSwipableHeaderView.h; sourceTree = "<group>"; };
759D088D1935BEEB00E930BD /* AXSampleSwipableHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AXSampleSwipableHeaderView.m; sourceTree = "<group>"; };
75A68DEA1935CBFD00C753FF /* AXSampleSwipableHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AXSampleSwipableHeaderView.xib; sourceTree = "<group>"; };
75CA85601989DB34006EC828 /* AXSub4ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AXSub4ViewController.h; sourceTree = "<group>"; };
75CA85611989DB34006EC828 /* AXSub4ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AXSub4ViewController.m; sourceTree = "<group>"; };
77E2F7A94B9542CEA1F1CC2F /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
CDE287AB09714778AADFE7FE /* Pods.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.xcconfig; path = Pods/Pods.xcconfig; sourceTree = "<group>"; };
/* End PBXFileReference section */
Expand All @@ -104,6 +109,7 @@
754814A3193234F400CFD342 /* UIKit.framework in Frameworks */,
7548149F193234F400CFD342 /* Foundation.framework in Frameworks */,
7777FECBD35D45B6935E3121 /* libPods.a in Frameworks */,
753E4CE6196148420051BFE2 /* Reveal.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -197,6 +203,7 @@
7548149D193234F400CFD342 /* Frameworks */ = {
isa = PBXGroup;
children = (
753E4CE5196148420051BFE2 /* Reveal.framework */,
7548149E193234F400CFD342 /* Foundation.framework */,
754814A0193234F400CFD342 /* CoreGraphics.framework */,
754814A2193234F400CFD342 /* UIKit.framework */,
Expand Down Expand Up @@ -269,6 +276,8 @@
754814E219323ABF00CFD342 /* AXSub2TableViewController.m */,
754814E419323ADF00CFD342 /* AXSub3ViewController.h */,
754814E519323ADF00CFD342 /* AXSub3ViewController.m */,
75CA85601989DB34006EC828 /* AXSub4ViewController.h */,
75CA85611989DB34006EC828 /* AXSub4ViewController.m */,
);
name = "Sub View Controllers";
sourceTree = "<group>";
Expand Down Expand Up @@ -459,6 +468,7 @@
757BA7B219338CC3006ADDAF /* AXSampleNavBarTabViewController.m in Sources */,
754814AF193234F400CFD342 /* AXAppDelegate.m in Sources */,
754814E619323ADF00CFD342 /* AXSub3ViewController.m in Sources */,
75CA85621989DB34006EC828 /* AXSub4ViewController.m in Sources */,
7584B665194A86C900AEE4DF /* AXSampleNoSwipableHeaderView.m in Sources */,
7584B66A194A875F00AEE4DF /* AXSampleNoSwipableTabViewController.m in Sources */,
75101E5E1936CE0F00AE76E8 /* AXSampleNoHeaderTabViewController.m in Sources */,
Expand Down Expand Up @@ -580,6 +590,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(SYSTEM_APPS_DIR)/Reveal.app/Contents/SharedSupport/iOS-Libraries",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "StretchableHeaderTabViewExample/StretchableHeaderTabViewExample-Prefix.pch";
INFOPLIST_FILE = "StretchableHeaderTabViewExample/StretchableHeaderTabViewExample-Info.plist";
Expand All @@ -594,6 +608,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(SYSTEM_APPS_DIR)/Reveal.app/Contents/SharedSupport/iOS-Libraries",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "StretchableHeaderTabViewExample/StretchableHeaderTabViewExample-Prefix.pch";
INFOPLIST_FILE = "StretchableHeaderTabViewExample/StretchableHeaderTabViewExample-Info.plist";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#import "AXSub1TableViewController.h"
#import "AXSub2TableViewController.h"
#import "AXSub3ViewController.h"
#import "AXSub4ViewController.h"
#import "AXSampleSwipableHeaderView.h"

@interface AXSampleNavBarTabViewController ()
Expand All @@ -27,8 +28,9 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
AXSub1TableViewController *sub1ViewCon = [[AXSub1TableViewController alloc] init];
AXSub2TableViewController *sub2ViewCon = [[AXSub2TableViewController alloc] init];
AXSub3ViewController *sub3ViewCon = [[AXSub3ViewController alloc] init];
AXSub4ViewController *sub4ViewCon = [[AXSub4ViewController alloc] init];

NSArray *viewControllers = @[sub1ViewCon, sub2ViewCon, sub3ViewCon];
NSArray *viewControllers = @[sub1ViewCon, sub2ViewCon, sub3ViewCon, sub4ViewCon];
self.viewControllers = viewControllers;
}
return self;
Expand Down
13 changes: 13 additions & 0 deletions Example/StretchableHeaderTabViewExample/AXSub4ViewController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// AXSub4ViewController.h
// StretchableHeaderTabViewExample
//

#import <UIKit/UIKit.h>
#import "AXStretchableHeaderTabViewController.h"

@interface AXSub4ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, AXStretchableSubViewControllerViewSource>

@property (strong, nonatomic) UITableView *tableView;

@end
Loading

0 comments on commit e311f1e

Please sign in to comment.