diff --git a/ExampleProject/Example.xcodeproj/project.pbxproj b/ExampleProject/Example.xcodeproj/project.pbxproj index 1e93f9c3..7d6f9600 100644 --- a/ExampleProject/Example.xcodeproj/project.pbxproj +++ b/ExampleProject/Example.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 484C209615F445EB00B3104E /* ExampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 484C208815F445EB00B3104E /* ExampleView.m */; }; 484C209715F445EB00B3104E /* large-image.jpeg in Resources */ = {isa = PBXBuildFile; fileRef = 484C208915F445EB00B3104E /* large-image.jpeg */; }; 484C209815F445EB00B3104E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 484C208A15F445EB00B3104E /* main.m */; }; + 500088B2165362E60067ED42 /* ExampleTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 500088B1165362E60067ED42 /* ExampleTableViewController.m */; }; 5C782FD013A54FFF00CF69EF /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C782FCF13A54FFD00CF69EF /* ApplicationServices.framework */; }; 5C782FD713A556A900CF69EF /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C782FD613A556A800CF69EF /* QuartzCore.framework */; }; 5ED56678139DC30300031CDF /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5ED56677139DC30300031CDF /* Cocoa.framework */; }; @@ -86,6 +87,8 @@ 484C208815F445EB00B3104E /* ExampleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExampleView.m; sourceTree = ""; }; 484C208915F445EB00B3104E /* large-image.jpeg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "large-image.jpeg"; sourceTree = ""; }; 484C208A15F445EB00B3104E /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 500088B0165362E60067ED42 /* ExampleTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExampleTableViewController.h; sourceTree = ""; }; + 500088B1165362E60067ED42 /* ExampleTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExampleTableViewController.m; sourceTree = ""; }; 5C782FCF13A54FFD00CF69EF /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = System/Library/Frameworks/ApplicationServices.framework; sourceTree = SDKROOT; }; 5C782FD613A556A800CF69EF /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 5ED56673139DC30300031CDF /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -124,6 +127,8 @@ 484C208415F445EB00B3104E /* ExampleTabBar.m */, 484C208515F445EB00B3104E /* ExampleTableViewCell.h */, 484C208615F445EB00B3104E /* ExampleTableViewCell.m */, + 500088B0165362E60067ED42 /* ExampleTableViewController.h */, + 500088B1165362E60067ED42 /* ExampleTableViewController.m */, 484C208715F445EB00B3104E /* ExampleView.h */, 484C208815F445EB00B3104E /* ExampleView.m */, 484C207915F445EB00B3104E /* MainMenu.xib */, @@ -308,6 +313,7 @@ 484C209515F445EB00B3104E /* ExampleTableViewCell.m in Sources */, 484C209615F445EB00B3104E /* ExampleView.m in Sources */, 484C209815F445EB00B3104E /* main.m in Sources */, + 500088B2165362E60067ED42 /* ExampleTableViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ExampleProject/Example/ExampleTableViewController.h b/ExampleProject/Example/ExampleTableViewController.h new file mode 100644 index 00000000..26c387f6 --- /dev/null +++ b/ExampleProject/Example/ExampleTableViewController.h @@ -0,0 +1,15 @@ +// +// ExampleTableViewController.h +// Example +// +// Created by Max Goedjen on 11/13/12. +// +// + +#import "TUIKit.h" + +@interface ExampleTableViewController : TUIViewController + +@property (nonatomic, strong) TUITableView *tableView; + +@end diff --git a/ExampleProject/Example/ExampleTableViewController.m b/ExampleProject/Example/ExampleTableViewController.m new file mode 100644 index 00000000..12f0581d --- /dev/null +++ b/ExampleProject/Example/ExampleTableViewController.m @@ -0,0 +1,139 @@ +// +// ExampleTableViewController.m +// Example +// +// Created by Max Goedjen on 11/13/12. +// +// + +#import "ExampleTableViewController.h" +#import "ExampleSectionHeaderView.h" +#import "ExampleTableViewCell.h" + +@implementation ExampleTableViewController + +- (void)loadView { + self.view = [[TUIView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)]; +} + +- (void)viewDidLoad { + self.tableView = [[TUITableView alloc] initWithFrame:self.view.frame]; + self.tableView.alwaysBounceVertical = YES; + self.tableView.dataSource = self; + self.tableView.delegate = self; + [self.tableView reloadData]; + self.tableView.maintainContentOffsetAfterReload = YES; + self.tableView.autoresizingMask = TUIViewAutoresizingFlexibleSize; + + TUILabel *footerLabel = [[TUILabel alloc] initWithFrame:CGRectMake(0, 0, _tableView.frame.size.width, 44)]; + footerLabel.alignment = TUITextAlignmentCenter; + footerLabel.backgroundColor = [NSColor clearColor]; + footerLabel.font = [NSFont fontWithName:@"HelveticaNeue-Bold" size:15]; + footerLabel.text = @"Example Footer View"; + self.tableView.footerView = footerLabel; + + [self.view addSubview:self.tableView]; +} + +- (NSInteger)numberOfSectionsInTableView:(TUITableView *)tableView { + return 8; +} + +- (NSInteger)tableView:(TUITableView *)table numberOfRowsInSection:(NSInteger)section { + return 25; +} + +- (CGFloat)tableView:(TUITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + return 50.0; +} + +- (TUIView *)tableView:(TUITableView *)tableView headerViewForSection:(NSInteger)section { + ExampleSectionHeaderView *header = [[ExampleSectionHeaderView alloc] initWithFrame:CGRectMake(0, 0, 100, 32)]; + TUIAttributedString *title = [TUIAttributedString stringWithString:[NSString stringWithFormat:@"Example Section %d", (int)section]]; + title.color = [NSColor blackColor]; + title.font = [NSFont fontWithName:@"HelveticaNeue-Bold" size:15]; + header.labelRenderer.attributedString = title; + + // Dragging a title can drag the window too. + [header setMoveWindowByDragging:YES]; + + // Add an activity indicator to the header view with a 24x24 size. + // Since we know the height of the header won't change we can pre- + // pad it to 4. However, since the table view's width can change, + // we'll create a layout constraint to keep the activity indicator + // anchored 16px left of the right side of the header view. + TUIActivityIndicatorView *indicator = [[TUIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 4, 24, 24) + activityIndicatorStyle:TUIActivityIndicatorViewStyleGray]; + [indicator addLayoutConstraint:[TUILayoutConstraint constraintWithAttribute:TUILayoutConstraintAttributeMaxX + relativeTo:@"superview" + attribute:TUILayoutConstraintAttributeMaxX + offset:-16.0f]]; + + // Add a simple embossing shadow to the white activity indicator. + // This way, we can see it better on a bright background. Using + // the standard layer property keeps the shadow stable through + // animations. + indicator.layer.shadowColor = [NSColor whiteColor].tui_CGColor; + indicator.layer.shadowOffset = CGSizeMake(0, -1); + indicator.layer.shadowOpacity = 1.0f; + indicator.layer.shadowRadius = 1.0f; + + // We then add it as a subview and tell it to start animating. + [header addSubview:indicator]; + [indicator startAnimating]; + + return header; +} + +- (TUITableViewCell *)tableView:(TUITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + ExampleTableViewCell *cell = reusableTableCellOfClass(tableView, ExampleTableViewCell); + + TUIAttributedString *s = [TUIAttributedString stringWithString:[NSString stringWithFormat:@"example cell %d", (int)indexPath.row]]; + s.color = [NSColor blackColor]; + s.font = [NSFont fontWithName:@"HelveticaNeue" size:15];; + [s setFont:[NSFont fontWithName:@"HelveticaNeue-Bold" size:15] inRange:NSMakeRange(8, 4)]; // make the word "cell" bold + cell.attributedString = s; + + return cell; +} + +- (void)tableView:(TUITableView *)tableView didClickRowAtIndexPath:(NSIndexPath *)indexPath withEvent:(NSEvent *)event { + if([event clickCount] == 1) { + // do something cool + ExampleTableViewController *pushed = [[ExampleTableViewController alloc] initWithNibName:nil bundle:nil]; + [self.navigationController pushViewController:pushed animated:YES]; + } + + if(event.type == NSRightMouseUp){ + // show context menu + } +} +- (BOOL)tableView:(TUITableView *)tableView shouldSelectRowAtIndexPath:(NSIndexPath *)indexPath forEvent:(NSEvent *)event{ + switch (event.type) { + case NSRightMouseDown: + return NO; + } + + return YES; +} + +-(BOOL)tableView:(TUITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { + // return YES to enable row reordering by dragging; don't implement this method or return + // NO to disable + return YES; +} + +-(void)tableView:(TUITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { + // update the model to reflect the changed index paths; since this example isn't backed by + // a "real" model, after dropping a cell the table will revert to it's previous state + NSLog(@"Move dragged row: %@ => %@", fromIndexPath, toIndexPath); +} + +-(NSIndexPath *)tableView:(TUITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)fromPath toProposedIndexPath:(NSIndexPath *)proposedPath { + // optionally revise the drag-to-reorder drop target index path by returning a different index path + // than proposedPath. if proposedPath is suitable, return that. if this method is not implemented, + // proposedPath is used by default. + return proposedPath; +} + +@end diff --git a/ExampleProject/Example/ExampleView.h b/ExampleProject/Example/ExampleView.h index 1b0a3a2a..c62725f2 100644 --- a/ExampleProject/Example/ExampleView.h +++ b/ExampleProject/Example/ExampleView.h @@ -17,13 +17,9 @@ #import "TUIKit.h" #import "ExampleTabBar.h" -@interface ExampleView : TUIView -{ - TUITableView *_tableView; - ExampleTabBar *_tabBar; - - NSFont *exampleFont1; - NSFont *exampleFont2; -} +@interface ExampleView : TUIView + +@property (nonatomic, strong) ExampleTabBar *tabBar; +@property (nonatomic, strong) TUINavigationController *navigationController; @end diff --git a/ExampleProject/Example/ExampleView.m b/ExampleProject/Example/ExampleView.m index 7d19e26a..4ad20337 100644 --- a/ExampleProject/Example/ExampleView.m +++ b/ExampleProject/Example/ExampleView.m @@ -15,8 +15,7 @@ */ #import "ExampleView.h" -#import "ExampleTableViewCell.h" -#import "ExampleSectionHeaderView.h" +#import "ExampleTableViewController.h" #define TAB_HEIGHT 60 @@ -27,35 +26,23 @@ - (id)initWithFrame:(CGRect)frame if((self = [super initWithFrame:frame])) { self.backgroundColor = [NSColor colorWithCalibratedWhite:0.9 alpha:1.0]; - // if you're using a font a lot, it's best to allocate it once and re-use it - exampleFont1 = [NSFont fontWithName:@"HelveticaNeue" size:15]; - exampleFont2 = [NSFont fontWithName:@"HelveticaNeue-Bold" size:15]; - CGRect b = self.bounds; b.origin.y += TAB_HEIGHT; b.size.height -= TAB_HEIGHT; + ExampleTableViewController *tableViewController = [[ExampleTableViewController alloc] initWithNibName:nil bundle:nil]; + _navigationController = [[TUINavigationController alloc] initWithRootViewController:tableViewController]; + [self addSubview:_navigationController.view]; + [_navigationController.view addLayoutConstraint:[TUILayoutConstraint constraintWithAttribute:TUILayoutConstraintAttributeWidth relativeTo:@"superview" attribute:TUILayoutConstraintAttributeWidth]]; + [_navigationController.view addLayoutConstraint:[TUILayoutConstraint constraintWithAttribute:TUILayoutConstraintAttributeHeight relativeTo:@"superview" attribute:TUILayoutConstraintAttributeHeight offset:-TAB_HEIGHT]]; + [_navigationController.view addLayoutConstraint:[TUILayoutConstraint constraintWithAttribute:TUILayoutConstraintAttributeMinX relativeTo:@"superview" attribute:TUILayoutConstraintAttributeMinX]]; + [_navigationController.view addLayoutConstraint:[TUILayoutConstraint constraintWithAttribute:TUILayoutConstraintAttributeMinY relativeTo:@"superview" attribute:TUILayoutConstraintAttributeMinY offset:TAB_HEIGHT]]; + /* Note by default scroll views (and therefore table views) don't have clipsToBounds enabled. Set only if needed. In this case we don't, so it could potentially save us some rendering costs. */ - _tableView = [[TUITableView alloc] initWithFrame:b]; - _tableView.alwaysBounceVertical = YES; - _tableView.autoresizingMask = TUIViewAutoresizingFlexibleSize; - _tableView.dataSource = self; - _tableView.delegate = self; - _tableView.maintainContentOffsetAfterReload = YES; - - TUILabel *footerLabel = [[TUILabel alloc] initWithFrame:CGRectMake(0, 0, _tableView.frame.size.width, 44)]; - footerLabel.alignment = TUITextAlignmentCenter; - footerLabel.backgroundColor = [NSColor clearColor]; - footerLabel.font = exampleFont2; - footerLabel.text = @"Example Footer View"; - _tableView.footerView = footerLabel; - - [self addSubview:_tableView]; - _tabBar = [[ExampleTabBar alloc] initWithNumberOfTabs:5]; _tabBar.delegate = self; // It'd be easier to just use .autoresizingmask, but for demonstration we'll use ^layout. @@ -123,114 +110,9 @@ - (void)tabBar:(ExampleTabBar *)tabBar didSelectTab:(NSInteger)index { NSLog(@"selected tab %ld", index); if(index == [[tabBar tabViews] count] - 1){ - NSLog(@"Reload table data..."); - [_tableView reloadData]; - } -} - -- (NSInteger)numberOfSectionsInTableView:(TUITableView *)tableView -{ - return 8; -} - -- (NSInteger)tableView:(TUITableView *)table numberOfRowsInSection:(NSInteger)section -{ - return 25; -} - -- (CGFloat)tableView:(TUITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath -{ - return 50.0; -} - -- (TUIView *)tableView:(TUITableView *)tableView headerViewForSection:(NSInteger)section -{ - ExampleSectionHeaderView *view = [[ExampleSectionHeaderView alloc] initWithFrame:CGRectMake(0, 0, 100, 32)]; - TUIAttributedString *title = [TUIAttributedString stringWithString:[NSString stringWithFormat:@"Example Section %d", (int)section]]; - title.color = [NSColor blackColor]; - title.font = exampleFont2; - view.labelRenderer.attributedString = title; - - // Dragging a title can drag the window too. - [view setMoveWindowByDragging:YES]; - - // Add an activity indicator to the header view with a 24x24 size. - // Since we know the height of the header won't change we can pre- - // pad it to 4. However, since the table view's width can change, - // we'll create a layout constraint to keep the activity indicator - // anchored 16px left of the right side of the header view. - TUIActivityIndicatorView *indicator = [[TUIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 4, 24, 24) - activityIndicatorStyle:TUIActivityIndicatorViewStyleGray]; - [indicator addLayoutConstraint:[TUILayoutConstraint constraintWithAttribute:TUILayoutConstraintAttributeMaxX - relativeTo:@"superview" - attribute:TUILayoutConstraintAttributeMaxX - offset:-16.0f]]; - - // Add a simple embossing shadow to the white activity indicator. - // This way, we can see it better on a bright background. Using - // the standard layer property keeps the shadow stable through - // animations. - indicator.layer.shadowColor = [NSColor whiteColor].tui_CGColor; - indicator.layer.shadowOffset = CGSizeMake(0, -1); - indicator.layer.shadowOpacity = 1.0f; - indicator.layer.shadowRadius = 1.0f; - - // We then add it as a subview and tell it to start animating. - [view addSubview:indicator]; - [indicator startAnimating]; - - return view; -} - -- (TUITableViewCell *)tableView:(TUITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - ExampleTableViewCell *cell = reusableTableCellOfClass(tableView, ExampleTableViewCell); - - TUIAttributedString *s = [TUIAttributedString stringWithString:[NSString stringWithFormat:@"example cell %d", (int)indexPath.row]]; - s.color = [NSColor blackColor]; - s.font = exampleFont1; - [s setFont:exampleFont2 inRange:NSMakeRange(8, 4)]; // make the word "cell" bold - cell.attributedString = s; - - return cell; -} - -- (void)tableView:(TUITableView *)tableView didClickRowAtIndexPath:(NSIndexPath *)indexPath withEvent:(NSEvent *)event -{ - if([event clickCount] == 1) { - // do something cool + NSLog(@"popping nav controller..."); + [self.navigationController popViewControllerAnimated:YES]; } - - if(event.type == NSRightMouseUp){ - // show context menu - } -} -- (BOOL)tableView:(TUITableView *)tableView shouldSelectRowAtIndexPath:(NSIndexPath *)indexPath forEvent:(NSEvent *)event{ - switch (event.type) { - case NSRightMouseDown: - return NO; - } - - return YES; -} - --(BOOL)tableView:(TUITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { - // return TRUE to enable row reordering by dragging; don't implement this method or return - // FALSE to disable - return TRUE; -} - --(void)tableView:(TUITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { - // update the model to reflect the changed index paths; since this example isn't backed by - // a "real" model, after dropping a cell the table will revert to it's previous state - NSLog(@"Move dragged row: %@ => %@", fromIndexPath, toIndexPath); -} - --(NSIndexPath *)tableView:(TUITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)fromPath toProposedIndexPath:(NSIndexPath *)proposedPath { - // optionally revise the drag-to-reorder drop target index path by returning a different index path - // than proposedPath. if proposedPath is suitable, return that. if this method is not implemented, - // proposedPath is used by default. - return proposedPath; } @end diff --git a/lib/UIKit/TUINavigationController.h b/lib/UIKit/TUINavigationController.h index 77373243..074fefba 100644 --- a/lib/UIKit/TUINavigationController.h +++ b/lib/UIKit/TUINavigationController.h @@ -9,19 +9,38 @@ #import #import "TUIViewController.h" +@class TUINavigationController; + +@protocol TUINavigationControllerDelegate + +- (void)navigationController:(TUINavigationController *)navigationController willShowViewController:(TUIViewController *)viewController animated:(BOOL)animated; +- (void)navigationController:(TUINavigationController *)navigationController didShowViewController:(TUIViewController *)viewController animated:(BOOL)animated; + +@end + @interface TUINavigationController : TUIViewController @property (nonatomic, readonly) TUIViewController *topViewController; @property (nonatomic, readonly) NSArray *viewControllers; +@property (nonatomic, assign) id delegate; + - (id)initWithRootViewController:(TUIViewController *)viewController; - (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated; +- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated completion:(void (^)(BOOL finished))completion; - (void)pushViewController:(TUIViewController *)viewController animated:(BOOL)animated; -- (TUIViewController *)popViewControlerAnimated:(BOOL)animated; +- (void)pushViewController:(TUIViewController *)viewController animated:(BOOL)animated completion:(void (^)(BOOL finished))completion; + +- (TUIViewController *)popViewControllerAnimated:(BOOL)animated; +- (TUIViewController *)popViewControllerAnimated:(BOOL)animated completion:(void (^)(BOOL finished))completion; + - (NSArray *)popToRootViewControllerAnimated:(BOOL)animated; +- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated completion:(void (^)(BOOL finished))completion; + - (NSArray *)popToViewController:(TUIViewController *)viewController animated:(BOOL)animated; +- (NSArray *)popToViewController:(TUIViewController *)viewController animated:(BOOL)animated completion:(void (^)(BOOL finished))completion; @end diff --git a/lib/UIKit/TUINavigationController.m b/lib/UIKit/TUINavigationController.m index 65420f26..7d12185b 100644 --- a/lib/UIKit/TUINavigationController.m +++ b/lib/UIKit/TUINavigationController.m @@ -15,7 +15,7 @@ @interface TUINavigationController () @end -static CGFloat const TUINavigationControllerAnimationDuration = 0.5f; +static CGFloat const TUINavigationControllerAnimationDuration = 0.25f; @implementation TUINavigationController @@ -24,6 +24,7 @@ - (id)initWithRootViewController:(TUIViewController *)viewController { if (self) { _stack = [@[] mutableCopy]; [_stack addObject:viewController]; + viewController.navigationController = self; self.view.clipsToBounds = YES; } return self; @@ -34,11 +35,20 @@ - (void)loadView { self.view.backgroundColor = [NSColor lightGrayColor]; TUIViewController *visible = [self topViewController]; + [visible viewWillAppear:NO]; + if ([self.delegate respondsToSelector:@selector(navigationController:willShowViewController:animated:)]) { + [self.delegate navigationController:self willShowViewController:visible animated:NO]; + } + [self.view addSubview:visible.view]; visible.view.frame = self.view.bounds; visible.view.autoresizingMask = TUIViewAutoresizingFlexibleSize; + [visible viewDidAppear:YES]; + if ([self.delegate respondsToSelector:@selector(navigationController:didShowViewController:animated:)]) { + [self.delegate navigationController:self didShowViewController:visible animated:NO]; + } } @@ -53,9 +63,12 @@ - (TUIViewController *)topViewController { } #pragma mark - Methods +- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated { + [self setViewControllers:viewControllers animated:animated completion:nil]; +} -- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated { - CGFloat duration = animated ? TUINavigationControllerAnimationDuration : 0; +- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated completion:(void (^)(BOOL finished))completion { + CGFloat duration = (animated ? TUINavigationControllerAnimationDuration : 0); TUIViewController *viewController = [viewControllers lastObject]; BOOL containedAlready = ([_stack containsObject:viewController]); @@ -63,34 +76,61 @@ - (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated { [CATransaction begin]; //Push if it's not in the stack, pop back if it is [self.view addSubview:viewController.view]; - viewController.view.frame = containedAlready ? TUINavigationOffscreenLeftFrame(self.view.bounds) : TUINavigationOffscreenRightFrame(self.view.bounds); + viewController.view.frame = (containedAlready ? TUINavigationOffscreenLeftFrame(self.view.bounds) : TUINavigationOffscreenRightFrame(self.view.bounds)); [CATransaction flush]; [CATransaction commit]; TUIViewController *last = [self topViewController]; + for (TUIViewController *controller in _stack) { + controller.navigationController = nil; + } [_stack removeAllObjects]; [_stack addObjectsFromArray:viewControllers]; + for (TUIViewController *controller in viewControllers) { + controller.navigationController = self; + } [TUIView animateWithDuration:duration animations:^{ - last.view.frame = containedAlready ? TUINavigationOffscreenRightFrame(self.view.bounds) : TUINavigationOffscreenLeftFrame(self.view.bounds); + last.view.frame = (containedAlready ? TUINavigationOffscreenRightFrame(self.view.bounds) : TUINavigationOffscreenLeftFrame(self.view.bounds)); viewController.view.frame = self.view.bounds; } completion:^(BOOL finished) { [last.view removeFromSuperview]; + [viewController viewDidAppear:animated]; + if ([self.delegate respondsToSelector:@selector(navigationController:didShowViewController:animated:)]) { + [self.delegate navigationController:self didShowViewController:viewController animated:animated]; + } + [last viewDidDisappear:animated]; + + if (completion) { + completion(finished); + } + }]; } - (void)pushViewController:(TUIViewController *)viewController animated:(BOOL)animated { + [self pushViewController:viewController animated:animated completion:nil]; +} + +- (void)pushViewController:(TUIViewController *)viewController animated:(BOOL)animated completion:(void (^)(BOOL finished))completion { TUIViewController *last = [self topViewController]; [_stack addObject:viewController]; - CGFloat duration = animated ? TUINavigationControllerAnimationDuration : 0; + viewController.navigationController = self; + + CGFloat duration = (animated ? TUINavigationControllerAnimationDuration : 0); [last viewWillDisappear:animated]; + + [viewController viewWillAppear:animated]; - + if ([self.delegate respondsToSelector:@selector(navigationController:willShowViewController:animated:)]) { + [self.delegate navigationController:self willShowViewController:viewController animated:animated]; + } + [self.view addSubview:viewController.view]; //Make sure the app draws the frame offscreen instead of just 'popping' it in @@ -104,29 +144,51 @@ - (void)pushViewController:(TUIViewController *)viewController animated:(BOOL)an viewController.view.frame = self.view.bounds; } completion:^(BOOL finished) { [last.view removeFromSuperview]; + [viewController viewDidAppear:animated]; + if ([self.delegate respondsToSelector:@selector(navigationController:didShowViewController:animated:)]) { + [self.delegate navigationController:self didShowViewController:viewController animated:animated]; + } + [last viewDidDisappear:animated]; + + if (completion) { + completion(finished); + } + }]; } -- (TUIViewController *)popViewControlerAnimated:(BOOL)animated { +- (TUIViewController *)popViewControllerAnimated:(BOOL)animated { + return [self popViewControllerAnimated:animated completion:nil]; +} + +- (TUIViewController *)popViewControllerAnimated:(BOOL)animated completion:(void (^)(BOOL finished))completion { if ([_stack count] <= 1) { NSLog(@"Not enough view controllers on stack to pop"); return nil; } TUIViewController *popped = [_stack lastObject]; - [self popToViewController:[_stack objectAtIndex:([_stack count] - 2)] animated:animated]; + [self popToViewController:_stack[([_stack count] - 2)] animated:animated completion:completion]; return popped; } - (NSArray *)popToRootViewControllerAnimated:(BOOL)animated { - if ([[self topViewController] isEqual:[_stack objectAtIndex:0]] == YES) { + return [self popToRootViewControllerAnimated:animated completion:nil]; +} + +- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated completion:(void (^)(BOOL finished))completion { + if ([[self topViewController] isEqual:_stack[0]] == YES) { return @[]; } - return [self popToViewController:[_stack objectAtIndex:0] animated:animated]; + return [self popToViewController:_stack[0] animated:animated completion:completion]; } - (NSArray *)popToViewController:(TUIViewController *)viewController animated:(BOOL)animated { + return [self popToViewController:viewController animated:animated completion:nil]; +} + +- (NSArray *)popToViewController:(TUIViewController *)viewController animated:(BOOL)animated completion:(void (^)(BOOL finished))completion { if ([_stack containsObject:viewController] == NO) { NSLog(@"View controller %@ is not in stack", viewController); return @[]; @@ -137,6 +199,7 @@ - (NSArray *)popToViewController:(TUIViewController *)viewController animated:(B NSMutableArray *popped = [@[] mutableCopy]; while ([viewController isEqual:[_stack lastObject]] == NO) { [popped addObject:[_stack lastObject]]; + [(TUIViewController *)[_stack lastObject] setNavigationController:nil]; [_stack removeLastObject]; } @@ -144,18 +207,32 @@ - (NSArray *)popToViewController:(TUIViewController *)viewController animated:(B [self.view addSubview:viewController.view]; viewController.view.frame = TUINavigationOffscreenLeftFrame(self.view.bounds); - CGFloat duration = animated ? TUINavigationControllerAnimationDuration : 0; + CGFloat duration = (animated ? TUINavigationControllerAnimationDuration : 0); [last viewWillDisappear:animated]; + [viewController viewWillAppear:animated]; + if ([self.delegate respondsToSelector:@selector(navigationController:willShowViewController:animated:)]) { + [self.delegate navigationController:self willShowViewController:viewController animated:animated]; + } [TUIView animateWithDuration:duration animations:^{ last.view.frame = TUINavigationOffscreenRightFrame(self.view.bounds); viewController.view.frame = self.view.bounds; } completion:^(BOOL finished) { [last.view removeFromSuperview]; + [viewController viewDidAppear:animated]; + if ([self.delegate respondsToSelector:@selector(navigationController:didShowViewController:animated:)]) { + [self.delegate navigationController:self didShowViewController:viewController animated:animated]; + } + [last viewDidDisappear:animated]; + + if (completion) { + completion(finished); + } + }]; diff --git a/lib/UIKit/TUIScrollView.m b/lib/UIKit/TUIScrollView.m index 76c7db24..67ff7631 100644 --- a/lib/UIKit/TUIScrollView.m +++ b/lib/UIKit/TUIScrollView.m @@ -137,6 +137,9 @@ - (id)initWithFrame:(CGRect)frame - (void)dealloc { + self.verticalScroller.scrollView = nil; + self.horizontalScroller.scrollView = nil; + if (displayLink) { CVDisplayLinkRelease(displayLink); } diff --git a/lib/UIKit/TUIViewController.h b/lib/UIKit/TUIViewController.h index 6965204e..f70514a1 100644 --- a/lib/UIKit/TUIViewController.h +++ b/lib/UIKit/TUIViewController.h @@ -17,12 +17,12 @@ #import "TUIResponder.h" @class TUINavigationItem; +@class TUINavigationController; @class TUIView; @interface TUIViewController : TUIResponder { TUIView *_view; - __unsafe_unretained TUIViewController *_parentViewController; // Nonretained } - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil; @@ -41,8 +41,7 @@ - (void)didReceiveMemoryWarning; -@property(nonatomic,weak) TUIViewController *parentViewController; // If this view controller is inside a navigation controller or tab bar controller, or has been presented modally by another view controller, return it. - -- (TUIView *)setupStandardView; // don't use this +@property (nonatomic, unsafe_unretained) TUIViewController *parentViewController; // If this view controller is inside a navigation controller or tab bar controller, or has been presented modally by another view controller, return it. +@property (nonatomic, unsafe_unretained) TUINavigationController *navigationController; @end diff --git a/lib/UIKit/TUIViewController.m b/lib/UIKit/TUIViewController.m index e2f5f8c0..72cc0cf6 100644 --- a/lib/UIKit/TUIViewController.m +++ b/lib/UIKit/TUIViewController.m @@ -88,28 +88,9 @@ - (void)didReceiveMemoryWarning // Called when the parent application receives a } } -- (TUIViewController *)parentViewController -{ - return _parentViewController; -} - -- (void)setParentViewController:(TUIViewController *)v -{ - _parentViewController = v; -} - - (TUIResponder *)initialFirstResponder { return _view.initialFirstResponder; } -- (TUIView *)setupStandardView -{ - TUIView *v = [[TUIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - v.backgroundColor = [NSColor colorWithCalibratedWhite:0.96 alpha:1.0]; - self.view = v; - - return v; -} - @end