Move UINavigationController's toolbar to the top to lie underneath navigation bar
Asked Answered
D

2

6

When you create a UINavigationController, you can reveal its default hidden UIToolbar via setToolbarHidden:animated: (or by checking Shows Toolbar in Interface Builder). This causes a toolbar to appear at the bottom of the screen, and this toolbar persists between pushing and popping of view controllers on the navigation stack. That is exactly what I need, except I need the toolbar to be located at the top of the screen. It appears that's exactly what Apple has done with the iTunes app:

enter image description here

How can one move UINavigationController's toolbar to the top to lie underneath the navigation bar instead of at the bottom?

I've tried to implement the UIToolbarDelegate, override positionForBar:, and return UIBarPosition.TopAttached or UIBarPosition.Top after setting the delegate of self.navigationController?.toolbar to self, but this did not even call the delegate method therefore it didn't change the bar position.

Note that I need the toolbar to be preserved between navigation, so I can't simply add a toolbar to a view controller and position it under the nav bar.

Danieldaniela answered 18/7, 2015 at 15:55 Comment(3)
Have you tried to change the toolbar.frame.origin.y value?Priapus
Did you ever find a proper solution to this problem?Thracian
@Thracian No I didn’t end up pursuing itDanieldaniela
G
0

The solution for this problem is a two (and a half) step process:

First you have to add an observer to the toolbars 'center' member.

Second, inside your observeValueForKeyPath:ofObject:change:context:, relocate the toolbar to your target position every time it is moved by somebody (e.g. the navigation controller itself for example, when the device rotates).

I did this in my UINavigationController subclass. To avoid recursion, I've installed an local flag member 'inToolbarFrameChange'.

The last (half) step was a bit tricky to find out... you've to access the toolbars 'frame' member, to get the observer to be called at all... I guess, the reason for this might be, that 'frame' is implemented as an method inside UIToolbar and the base 'frame' value in UIView is only updated when the UIToolbar method is called ?!?

I did implement this 'frame' access in my overloaded setToolbarHidden:animated: method, which does nothing but to forward the call and to access the toolbars 'frame' value.

@interface MMMasterNavigationController ()
@property (assign, nonatomic) BOOL          inToolbarFrameChange;
@end

@implementation MMMasterNavigationController

/*
 awakeFromNib

 */
- (void)awakeFromNib {

    [super awakeFromNib];

    // ... other inits

    self.inToolbarFrameChange = NO;
}

/*
 viewDidLoad

 */
- (void)viewDidLoad {

    [super viewDidLoad];

    // 'center' instead of 'frame' from: https://mcmap.net/q/982809/-ios-adding-observer-to-a-uiview-39-s-frame-origin-y
    [self.toolbar addObserver:self
                   forKeyPath:@"center"
                      options:NSKeyValueObservingOptionNew 
                      context:0];
}

/*
 observeValueForKeyPath:ofObject:change:context:

 */
- (void)observeValueForKeyPath:(NSString *)pKeyPath
                      ofObject:(id)pObject
                        change:(NSDictionary<NSString *,id> *)pChange
                       context:(void *)pContext {

    if ([pKeyPath isEqualToString:@"center"]) {
        if (!self.inToolbarFrameChange) {
            //NSLog(@"%s (0): %@", __PRETTY_FUNCTION__, pChange);
            self.inToolbarFrameChange = YES;

            CGRect  tbFrame = self.toolbar.frame;
            // maybe some other values are needed here for you
            tbFrame = CGRectMake(0, 0, CGRectGetWidth(tbFrame), CGRectGetHeight(tbFrame));
            self.toolbar.frame = tbFrame;

            self.inToolbarFrameChange = NO;
        }
    } else {
        [super observeValueForKeyPath:pKeyPath ofObject:pObject change:pChange context:pContext];
    }
}

/*
 setToolbarHidden:animated:

 */
- (void)setToolbarHidden:(BOOL)pHidden
                animated:(BOOL)pAnimated {
    FLog;

    [super setToolbarHidden:pHidden animated:NO];

    // Access the 'frame' member to let to observer fire
    CGRect  rectTB = self.toolbar.frame;
    rectTB = CGRectZero;
}
Gillespie answered 13/3, 2016 at 14:38 Comment(0)
V
0

You may create not UITableViewController but UIViewController. In view of UIViewController place UIToolBar below NavigationBar and UITableView. Delegate all necessary list of UITableView to UIViewController and thats all. Why should you use UIViewController instead UITableViewController? Because tableView will not have statical positions elements. You should have something that not contains ScrollView. In this situation it is only UIView. Also you may do some hack with UIScrollView of tableView but I think described method is easer.

Venom answered 7/2, 2017 at 12:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.