I'm building an iOS app using a NavigationController. However, in this app I need a sub navigation bar and it needs to be in every view. I initially implemented this using a toolbar in every view. But what happens, when a new view slides in, is that the toolbar slides in as well. I need the toolbar to be persistent, like the navigation bar. What's the best way to approach this?
My best guess is to set [self.navigationController setToolbarHidden:YES animated:YES]; to YES and then somehow positioning that underneath the navbar...
This is how I used to do it in every viewcontroller:
#interface TableViewController ()
#property (weak, nonatomic) IBOutlet UIToolbar *mainToolBar;
#end
#implementation TableViewController
- (void)viewWillAppear:(BOOL)animated
{
[self.navigationController setToolbarHidden:YES animated:YES];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"TV Shows";
[self addRightMenuButton];
[self loadNinjas];
self.mainToolBar.barTintColor = [UIColor whiteColor];
self.mainToolBar.layer.shadowColor = [[UIColor blackColor] CGColor];
self.mainToolBar.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
self.mainToolBar.layer.shadowRadius = 3.0f;
self.mainToolBar.layer.shadowOpacity = 1.0f;
self.tableView.dataSource=self;
self.tableView.delegate=self;
}
Three distinct approaches I can think of trying:
Make a taller Navigation Bar subclass with your secondary toolbar thing at the bottom. Set it as the Navigation Controller's Navigation Bar. I've tried this a couple of times to achieve different things and it didn't work out, but maybe it will for you.
Stick with what you're doing, a second toolbar on every VC, and use a custom VC transition or presentation controller to make it appear like the toolbar isn't moving while the rest of the content is.
Don't do this. I can't really see a way that a double navigation thing is going to be good for an iPhone app.
Related
I want to design an app with UI like this:
At bottom, I know it is UITabbarController. In each tab, it has many view controllers like BEAT, TOP, FUN... Each tab has different view controllers.
When scroll horizontal, can change from BEAT to TOP to FUN...
How can I design like that? What view controller should I use? It seems like UIPageController in UITabController, but with UIPageController, I don't know how to replace dots at bottom (.) by BEAT, TOP, FUN... at top.
Thanks for your help.
Alternative solution is to add UISwipeGesture(Left/Right) on view and upon swipe action you can push or pop view controllers.
In ViewController.h
#property (retain, nonatomic) UISwipeGestureRecognizer * leftSwipe;
#property (retain, nonatomic) UISwipeGestureRecognizer * rightSwipe;
In ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
[self setupSwipeGestures];
}
-(void)setupSwipeGestures
{
_leftSwipe=[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(next)];
[_leftSwipe setDirection:UISwipeGestureRecognizerDirectionLeft];
_rightSwipe =[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(previous)];
[_rightSwipe setDirection:UISwipeGestureRecognizerDirectionRight];
[self.view addGestureRecognizer:_leftSwipe];
[self.view addGestureRecognizer:_rightSwipe];
}
- (void) previous
{
// perform pop to get previous viewController
// i.e [self.navigationController popViewControllerAnimated:YES];
}
- (void) next
{
// perform push to get next viewController
// i.e [self.navigationController pushViewController:viewController animated:YES];
}
If you want to use above code in every Controller, then you may define your own viewController and paste above code in it, after that inherit all of your viewControllers where you need above functionality.
You can use XLPagerTabStrip library in each tab of your tab controller. Awesome library, even gives you the swiping feature similar to android which can enable you to swipe through individual page sections as well as keeping tab property.
I have a ViewController (with a container view) embedded in a Navigation controller. The container contains a pageViewController with one of the 'pages' being a TableViewController (with UITableView outlet: 'aTableView'). I want to trigger the edit mode in the tableViewController when tapping a custom editButton in the navigation bar. When I create a custom editutton in the tableViewController the edit mode works as expected, but when I use the custom editButton in the navigation bar the setEditing bool value remains zero even when I setEditing to YES in the editButton selector. Here's the code:
ViewController.m
-(void)viewDidLoad {
self.editBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[self.editBtn setFrame:CGRectMake(0, 0, 40, 22)];
[self.editBtn addTarget:self action:#selector(goToToggleEdit:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *editButton=[[UIBarButtonItem alloc]initWithCustomView:self.editBtn];
self.navigationItem.rightBarButtonItem = editButton;
}
-(void)goToToggleEdit:(id)sender
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
TableViewController *tvc = [storyboard instantiateViewControllerWithIdentifier:#"aTableViewController"];
if(something==foo){
[tvc toggleEdit];
}
}
aTableViewController.h
#interface aTableViewController : UITableViewController <UITextFieldDelegate> {
IBOutlet UITableView *aTableView;
}
-(void) toggleEdit ;
#end
aTableViewController.m
-(void)toggleEdit {
[aTableView setEditing:YES animated:YES];
NSLog(aTableView.editing ? #"Yes" : #"No"); // --> logss 'No'.
if (aTableView.editing){
//do something
}
else {
//do something else
}
}
How can I efficiently trigger the edit mode in the tableViewController this way?
Edit
#Bagrat Kirakosian pointed out to me that my view hierarchy (Navigation Controller > View Controller (with containter) > Page View Controller (in container) > Table View Controller) might be the problem. I just want to create a Navigation Bar (with an edit button) that is fixed, therefore I can't embed the Table View Controller directly in a Navigation Controller.
Thanks.
UPDATE: Solution
I have accepted #sebastien's solution although both #sebastien's and #Bagrat's solution work great. #Bagrat's answer includes direct access to the Table View Controllers, while #sebastien's solution calls edit mode in the pageViewController. I think, considering the tricky hierarchy, the latter is a bit more secure.
Here is the code for my View Controller that totally work fine. be sure you configure your bar button in the right method of View Controller lifecycle. Also be sure that your #selector is properly implemented in your code.
In the same View Controller put these two blocks of code
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
UIBarButtonItem *rightBarButton = [[UIBarButtonItem alloc] initWithImage:[[UIImage imageNamed:#"edit_icon.png" ] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] style:UIBarButtonItemStylePlain target:self action:#selector(edit:)];
[rightBarButton setTintColor:[UIColor whiteColor]];
self.navigationItem.rightBarButtonItem = rightBarButton;
}
Afterwards you need also to put your edit selector
-(void)edit:(UIButton *)sender {
// Toggle edit by inverting current edit state
// Also in this block change your right bar button text or image
[self.tableView setEditing:!self.tableView.editing animated:YES];
}
UPDATE 1
After your comment we got to whole another question. You problem is not in the part where you try to call toggle edit. Your problem is the wrong hierarchy of controllers (Navigation Controller > View Controller > Page View Controller > Table View Controller). This might cause a problem. Try to change your controllers like this;
UINavigationController > UIPageViewController > UIViewController(s)
Also it's a good practice to have a UITableView in UIViewController rather than using really dead UITableViewController. Don't forget to connect your tableView IBOutlet (by the way you didn't need it in UITableViewController), also connect datasource and delegate to Files owner. In your MyTableViewVC.h file add this line
#interface MyTableViewVC : UIViewController <UITableViewDataSource, UITableViewDelegate>
After that all your calls will work fine.
UPDATE 2
After analyzing your entire structure I found a mistake that you do every time on the button click.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
tvc = [[TodolistTableViewController alloc] init];
When you call storyboard every time it's ok but when you do [[TodolistTableViewController alloc] init] you are RE-MAKING the same table view controller every time but not even adding it to your main view. As I told you, your tricky hierarchy might cause difficulties but it has a solution.
In your PageViewController.m make tv1 and tv2 properties in .h file, like so.
#property (strong, nonatomic) UITableViewController *tv1;
#property (strong, nonatomic) UITableViewController *tv2;
Then in the view controller file do this
-(void)toggleEdit:(id)sender
{
PageViewController *current = (PageViewController *)[[self childViewControllers] firstObject];
if ([current isKindOfClass:[PageViewController class]])
{
[((TodolistTableViewController *)[current tv1]) toggleEdit];
}
}
Answer includes all security checks and direct access to your table view controllers, because you might need to change other properties/call functions later.
Now in -(void)toggleEdit:(id)sender you don't re-create your views every time but you catch the ones you already have in your current View Controller.
Good Luck!
Ok, your issue here is that you are trying to access an embedded controller in a wrong way.
You are actually managing 2 differents PageViewController:
The one you generated through your storyboard
The other one you are initiating in your code further
That's why you can't reach the expected result.
First of all, add a new method to your PageViewController:
PageViewController.h:
- (void)editTableAtIndex:(int)index;
PageViewController.m:
- (void)editTableAtIndex:(int)index {
[[self viewControllerAtIndex:index] setEditing:YES];
}
Now, in your main ViewController, access the PageViewController by using childViewControllers:
-(void)toggleEdit:(id)sender
{
PageViewController *pvc = self.childViewControllers[0];
[pvc editTableAtIndex:0];
}
It should be editing your TodoListTableView:
(Please notice that I used [pvc editTableAtIndex:0];, instead you should be calling something like [pvc editTableAtIndex:_PageViewController_current_index_];)
I have trouble w/ hiding toolbar when leave screen by segue.
Details: App has a few dozen screens, all of them are belonged the same navigation controller. A few screens have toolbar. For these a few screens in -(void)viewDidLoad I use
[self.navigationController setToolbarHidden:NO animated:NO];
and in -(void)viewWillDisappear:(BOOL)animated:
[self.navigationController setToolbarHidden:YES animated:YES];
so toolbar is visible only on necessary screens and the screen which needs toolbar controls the visibility. All work well when I navigate by "back" button.
The trouble starting when I try to navigate by segue like this (goto home & goto another branch).
[owner.navigationController popToRootViewControllerAnimated:NO];
[self performSegueWithIdentifier:SEGUE_NAME sender:self];
toolbar is stay visible in spite of calling -(void)viewWillDisappear which should hide toolbar.
are there any ideas how to perform these "ToolBarHidden" by right way.
thanks
PS: of course I could hide toolbar on every screen, but I want to avoid this unnecessary operations and want to know how to do it right.
**STEP1:** in your controller.h
#interface ViewController : UIViewController {
UIToolbar *bar;
}
#property (nonatomic, strong) IBOutlet UIToolbar *bar;
#end
**STEP2:** in your controller.m
#synthesize bar;
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.bar.hidden = NO;
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
self.bar.hidden = YES;
}
**STEP3:** connect in Intrface
hope this help you!
I'm currently trying to make a small demo app which is supposed to present 2 views at the same time :
- The first one, BaseViewController, is a classic controller with 2 buttons
- The second one, OverViewController, is launch on top of baseView and contains
a last button, and is supposed to be transparent and sending the touch controls
to the first one
Both viewController have their xib. To make it clear : rootView --> baseView + overView (transparent)
My problem is NOT to send the control events from the 2nd view to the first, but to make the 2nd view transparent and functionnal.
Here's what I've tried so far :
1) ------------ Presenting the two view controllers ------------
From the app rootViewController :
- (IBAction)buttonClicked:(id)sender
{
OverViewController *overVC = [[OverViewController alloc] init];
[overVC presentViewControllerFrom:self];
}
From OverViewController :
- (id)init
{
self = [super init];
if (self)
{
self.base = [[BaseViewController alloc] initWithNibName:#"BaseViewController" bundle:nil];
}
return self;
}
I made a custom method to make overView present the baseView before showing up.
- (void)presentViewControllerFrom:(RootViewController *)sender
{
[sender presentViewController:self.base animated:NO completion:nil];
self.view.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.0];
self.view.opaque = NO;
[self.base presentViewController:self animated:YES completion:nil];
}
At this point, the baseView appears, followed by the overView. The button of overView work correctly. Great ! But then the background of overView doesn't show what's supposed to be behind and stays black. No alpha transparency.
2) ------------ Presenting the baseView and adding overView as a subview ------------
From the app rootViewController (same than the one before) :
- (IBAction)buttonClicked:(id)sender
{
OverViewController *overVC = [[OverViewController alloc] init];
[overVC presentViewControllerFrom:self];
}
From OverViewController (not presenting but adding subview) :
- (void)presentViewControllerFrom:(RootViewController *)sender
{
[sender presentViewController:self.base animated:NO completion:nil];
self.view.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.0];
self.view.opaque = NO;
[self.base.view addSubview:self.view];
}
This time, the transparency is great but any touch on the overView button cause a EXC_BAD_ACCESS to show up.
There I am, wondering how to do this. Any help will be greatly appreciated ! :)
Don't use a combination of presenting a view controller and adding subviews. Choose 1. It should be subview that you choose. It can be owned by another controller and that controller should probably be added as a child view controller.
In your first situation you see black because iOS is removing the (now expected to not be visible) view from the view hierarchy. So your view is transparent but what you expect to be behind it is no longer there.
Your second situation is probably just resulting in an invalid view hierarchy somewhere.
The overview controller should not own the base controller. The base controller should be shown and then the overview controller view added as a subview.
I am trying to implement a push-up UINavigationBar, where the position of the navigation bar is attached to the contentOffset of the UIScrollView (similar to how safari works in ios7).
In order to get the dynamic movement working I am using a UINavigationBar created programatically and added as a subview of the UIViewController's view (it is accessible as self.navbar).
The UIViewController is within a UINavigationController hierarchy, so I am hiding the built-in self.navigationController.navigationBar at the top of -viewWillAppear:.
The problem I am trying to solve is to add a back button to this new standalone navbar. I would preferably like to simply copy the buttons or even the navigationItems from the navigationController and its hidden built-in navbar, but this doesnt seem to work
Is my only solution to set leftBarButtonItem on my standalone navbar to be a fake back button (when there is a backItem in the navController's navbar)? This seems a bit hacky, and I'd rather use the built backButton functionality.
Another way to do that, once you have your own UINavigationBar set, is to push two UINavigationItems on your navigationBar, causing back button to appear. You can then customize what happens when the back button is pressed.
Here's how I did that
1 - Some UINavigationItem subclass, to define extra-behavior / customization parameters
#interface MyNavigationItem : UINavigationItem
//example : some custom back action when 'back' is pressed
#property (nonatomic, copy) void (^onBackClickedAction)(void);
#end
2 - Then wire that into your UINavigationBarDelegate :
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
if ([item isKindOfClass:[MyNavigationItem class]]) {
MyNavigationItem *navItem = (MyNavigationItem *)item;
//custom action
if (navItem.backAction) {
navItem.backAction();
}
return YES;// return NO if you don't want your bar to animate to previous item
} else {
return YES;
}
}
You could adapt that scheme, calling your UINavigationController pop method on back action.
This is still hacky
Vinzzz' answer was a good solution. Here is my implementation, as the context was slightly different.
In the UIViewController's viewDidLoad method I setup my navbar's navigation items like this:
NSMutableArray* navItems = [#[] mutableCopy];
if (self.navigationController.viewControllers.count > 1)
{
NSInteger penultimateIndex = (NSInteger)self.navigationController.viewControllers.count - 2;
UIViewController* prevVC = (penultimateIndex >= 0) ? self.navigationController.viewControllers[penultimateIndex] : nil;
UINavigationItem* prevNavItem = [[UINavigationItem alloc] init];
prevNavItem.title = prevVC.title;
[navItems addObject:prevNavItem];
}
UINavigationItem* currNavItem = [[UINavigationItem alloc] init];
... <Add any other left/right buttons to the currNavItem> ...
[navItems addObject:currNavItem];
[self.navbar setItems:navItems];
...where self.navbar is my floating stand-alone UINavigationBar.
I also assign the current view controller to be self.navbar's delegate, and then listen for the -navigationBar:shouldPopItem: event that is triggered when the back button is pressed:
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
if (navigationBar == self.navbar)
{
[self.navigationController popViewControllerAnimated:YES];
return NO;
}
return YES;
}
(If you return YES, it will crash when a swipe gesture is used in ios7).