Navigation Bar partially hidden only in iOS5 - iphone

I have a Tab Bar Controller with Nav Controller. In this Nav Controller have a Navigation Bar.
The implementation it's very simple, there are not custom code. I do all with graphical interface in Xcode.
If I open the application in iOS4 it's perfect.
But If I open in iOS5 or iOS 5.0.1 ... It's partially hidden with status bar.
How I can fix it?
EDIT: This is my implementation in MainWindow.xib (sorry I need remove some names)

I Had the same problem with an old project made by other devs who used the xib.
My solution is that you have to write some code:d if you did the drag and drop programing approach.
So:
In viewWillAppear move all your ui elements with 20 pixels down.
I added tags in xib for each element and with a static int I keep tract of changes to be done only once.
static int oneTine = 0;
if(!oneTine){
for(int index = 1001; index<=1005; index++){
UIView *currentView = [self.view viewWithTag:index];
CGRect viewFrame = currentView.frame;
viewFrame.origin.y -=20;
currentView.frame = viewFrame;
}
oneTine = 1;
}

Related

NavigationBar hides TableView in TabBarViewController

I have a ViewController that Pushes a TabBarViewController. Inside that TabBar View Controller I have 4 tabs. Two of these tab bars are UITableViewControllers and the other two are ViewControllers. The first tab is a table view controller and is working fine, ie not being hidden by the navigation bar. The third tab, which is another TableViewController, is being partially covered by the navigation bar. The first section and first cell is being hidden underneath the navigation bar. Has anyone had this problem in the past or does anyone know a solution to this? I've tried a couple of things like resizing the frame size manually
self.tableView.frame = CGRectMake(10,10,self.view.bounds.size.width -20, self.view.bounds.size.height-20);
That did't seem to work. I tried AutoLayout as well and didn't work. I don't know what else to do. Anyone have any suggestions or ideas of how to tackle this.
Edit: I've tried the edgesForExtendedLayout but it is making my navigationbar a darker color. It animates the color change in the navigationbar, sort of like a loading bar.
Note: This is only happening in ios7. I just simulated it in iOS 6.1 and the navigationbar does not cover the table view controller at all, which is weird to me. Any one have any suggestions?
Edit #2: Noticing that this is an iOS 7 > problem i did the following but now the navigation bar has changed color to a darker color.
if([[[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:#"."][0] intValue] >= 7)
{
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
{
self.edgesForExtendedLayout = UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars = YES;
self.automaticallyAdjustsScrollViewInsets = NO;
}
}
In the viewDidLoad method of UITableViewController (assuming it's loaded with a call to the tabBarController) that is associated with the tabs use
if ([self respondsToSelector:#selector(edgesForExtendedLayout)]) {
self.edgesForExtendedLayout = UIRectEdgeNone;
self.tableView.contentInset = UIEdgeInsetsMake(0., 0., CGRectGetHeight(self.tabBarController.tabBar.frame), 0);
}
I fixed that with:
if ([self respondsToSelector:#selector(edgesForExtendedLayout)]) {
viewcontroller.edgesForExtendedLayout = UIRectEdgeNone;
viewcontroller.extendedLayoutIncludesOpaqueBars = NO;
viewcontroller.automaticallyAdjustsScrollViewInsets = NO;
}

UIScrollView referenced both by UIViewController's 'view' property and by an outlet

I'm about to add a UIScrollView to my iPhone project and before I implement this functionality I wanted to check if my approach is the right one or if I could be violating some best practice I'm not aware of.
The tutorials I've seen generally involve adding a UIScrollView to an existing UIView and they work from there. However, I was wondering if I could spare the UIView altogether and just add the UIScrollView as the only top-level object in my nib file.
My sample project uses Xcode's View-based Application template:
Project navigator http://img542.imageshack.us/img542/5364/projectnavigator.png
I deleted the UIView top-level object from the original MySampleViewController.xib file and replaced it by adding a UIScrollView object:
Nib placeholders http://img526.imageshack.us/img526/7709/placeholderobjects.png
Now my nib file only shows this object in the canvas:
Canvas http://img233.imageshack.us/img233/4063/scrollview.png
Then I created the link from the UIViewController's view outlet to the UIScrollView.
Now, if I wanted to programmatically manipulate the contents of the UIScrollView I can use this code:
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *colors = [NSArray arrayWithObjects:[UIColor redColor], [UIColor greenColor], [UIColor blueColor], nil];
// Solution B: With the following line we avoid creating an extra outlet linking to the UIScrollView top-level object in the nib file
UIScrollView *scrollView = (UIScrollView *)self.view;
for (int i = 0; i < colors.count; i++) {
CGRect frame;
//frame.origin.x = self.scroller.frame.size.width * i; // Solution A: scroller is an IBOutlet UIScrollView *scroller;
frame.origin.x = scrollView.frame.size.width * i; // Solution B
frame.origin.y = 0;
//frame.size = self.scroller.frame.size; // Solution A
frame.size = scrollView.frame.size; // Solution B
UIView *subView = [[UIView alloc] initWithFrame:frame];
subView.backgroundColor = [colors objectAtIndex:i];
//[self.scroller addSubview:subView]; // Solution A
[self.view addSubview:subView]; // Solution B
[subView release];
}
//self.scroller.contentSize = CGSizeMake(self.scroller.frame.size.width * colors.count, self.scroller.frame.size.height); // Solution A
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * colors.count, scrollView.frame.size.height); // Solution B
}
In order to implement Solution A the scroller outlet must be linked to the nib's UIScrollView as well, and the Connections Inspector looks like this:
Connections Inspector http://img228.imageshack.us/img228/8397/connectionsj.png
Solution A requires an outlet and this means having two connections to the UIScrollView: the UIViewController's own view outlet and MySampleViewController's scroller outlet. Is it legal and/or recommended to have two outlets pointing to the same view?
Solution B only involves UIViewController's view outlet linking to the view, and using this line:
UIScrollView *scrollView = (UIScrollView *)self.view;
My questions:
Do I incur in some sort of violation of Apple's design guidelines by using one of these two solutions?
Should I stick to the UIScrollView within a UIView solution?
Is there any other way to implement this?
Thanks!
P.S. Sorry for the syntax highlight, SO didn't recognize the use of the 'objective-c' tag
No I think you are fine either way.
I would, I don't think a UIView has any significant cost, plus what if you want to add a page control? and you don't have to cast the controller's view to a UIScrollView every time you need it.
Looks like you have it under control to me.
Solution A requires an outlet and this means having two connections to the UIScrollView: the UIViewController's own view outlet and MySampleViewController's scroller outlet. Is it legal and/or recommended to have two outlets pointing to the same view?
It standard to have IBOutlets to any view defined in your .nib that you want to access directly from your view controller.
If you don't want two outlets you could give the scroll view a tag then find it like so:
UIScrollView *myScrollView = (UIScrollView *)[self.view viewWithTag:1]
Then you only have the view as an outlet, but I would just add the extra outlet. Just make sure you set them to nil in your viewDidUnload.
Also you don't have to retain the scroll view (if you are even still using retain/release). Since the scroll view is inside your view controller's view it keeps a reference so you can have your scrollview's property by assign or week if your using ARC.
Hope that helps.

How do I get the width of a UITabBarItem?

The width of a UITabBarItem varies, depending on how many there are.
How do I determine how wide my tab bar items are?
I'm looking for a property if possible, as opposed to a mathematical formula, since on the iPad, there is also an issue of padding on either side of the tab bar. Consider these screenshots. Notice the padding on either side of the tab bar items on the iPad (highlighted with the red boxes). This padding does not exist on the iPhone.
The iPad:
The iPhone:
Edit: It has been noted in the comments below that this solution did not work in a beta version of iOS 5, so be prepared to modify it to meet your needs.
I think in order to do this the right way, you have to be able to get to the frame of each tab bar item. Fortunately, this is possible:
CGFloat tabBarTop = [[[self tabBarController] tabBar] frame].origin.y;
NSInteger index = [[self tabBarController] selectedIndex];
CGFloat tabMiddle = CGRectGetMidX([[[[[self tabBarController] tabBar] subviews] objectAtIndex:index] frame]);
The above code gives you the y coordinate of the top of the tab bar and the x coordinate of the middle of the selected tab item. From here, you can add your indicator view to the tab bar controller's view relative to this point.
Disclaimer: The danger with this approach is that it peeks under the hood of the UITabBar class by accessing its view hierarchy and the frame property of instances of the private UITabBarButton class. It is possible (however unlikely) that this approach could be affected/broken by a future iOS update. However, you're not accessing any private APIs, so this shouldn't get your app rejected from the App Store.
I made a simple iPad project demonstrating how it works, complete with animations.
Swift 3
I created a shared instance in UIApplication then pulled my subviews width
tabBarController?.tabBar.subviews[1].frame.width
UITabBarItem inherits from UIBarItem, which inherits from NSObject. Since it doesn't inherit from UIView, I don't know that you're going to be able to get the information you want just with a property. I know you don't want a mathematical way of doing it, but it would seem that getting the frame of the tab bar and dividing by the # of tabs would be a reasonable option that should work regardless of the hardware (iphone vs ipad). Padding shouldn't affect this, right? So you'd have:
tabSize = tabbar.frame.size.width / [tabbar.items count];
tabBarStart = tabbar.frame.origin.x;
// Assume index of tabs starts at 0, then the tab in your pic would be tab 4
// targetX would be the center point of the target tab.
targetX = tabBarStart + (tabSize * targetTabIndex) + (tabSize / 2);
I, as well, am interested to know if someone finds a simple property to give this information.
Looking at the answer above:
Items in the UITabBar work different in the iPhone and the iPad. In the iPhone they fill whole width, in the iPad they're centered horizontally.
There is a spacing between the items in the iPad.
You can set the values for both width and spacing using tabBar.itemWidth or tabBar.itemSpacing. You are not able to read system spacing or width from it - you'll receive 0 unless you set it.
So here's my sample how to get frames of all UITabBarItems:
// get all UITabBarButton views
NSMutableArray *tabViews = [NSMutableArray new];
for (UIView *view in self.tabBar.subviews) {
if ([view isKindOfClass:[UIControl class]]) {
[tabViews addObject:[NSValue valueWithCGRect:view.frame]];
}
}
// sort them from left to right
NSArray *sortedArray = [tabViews sortedArrayUsingComparator:^NSComparisonResult(NSValue *firstValue, NSValue *secondValue) {
CGRect firstRect = [firstValue CGRectValue];
CGRect secondRect = [secondValue CGRectValue];
return CGRectGetMinX(firstRect) > CGRectGetMinX(secondRect);
}];
Now you have a table of frames, corresponding to your tabbar items. You can eg. place an imageView on selected index button frame.
CGRect frame = self.tabBar.bounds;
CGSize imageSize = CGSizeMake(CGRectGetWidth(frame) / self.tabBar.items.count, self.imageView.image.size.height);
CGRect selectedRect = sortedArray.count > self.selectedIndex ? [sortedArray[self.selectedIndex] CGRectValue] : CGRectZero;
[self.imageView setFrame:CGRectIntegral(CGRectMake(CGRectGetMinX(selectedRect), CGRectGetMaxY(frame) - imageSize.height,
CGRectGetWidth(selectedRect), imageSize.height))];
I tried these 2 properties of UITabBar:
#property(nonatomic) CGFloat itemWidth NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;
#property(nonatomic) CGFloat itemSpacing NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;
But get zero value as Vive said. So I wrote some codes to get the right width:
CGFloat tabBarItemWidth = 0;
for (UIView *view in [self.tabBarController.tabBar subviews]) {
if ([view isKindOfClass:NSClassFromString(#"UITabBarButton")]) {
if (tabBarItemWidth == 0) {
tabBarItemWidth = view.frame.origin.x;
} else {
tabBarItemWidth = view.frame.origin.x - tabBarItemWidth;
break;
}
}
}
Why not use the width of first UITabBarButton? Because there are spaces between tab bar buttons. :)
You can get tabBarItem view using private property view. Then just get view frame.
- (CGRect)rectForTabBarItem:(UITabBarItem *)tabBarItem {
UIView *itemView = [tabBarItem valueForKey:#"view"];
return itemView.frame;
}
Loop through your tabBar's subviews and find the views with the class "UITabBarButton". These are the views for each tab item.
for (UIView *view in self.tabBar.subviews) {
if ([view isKindOfClass:NSClassFromString(#"UITabBarButton")]) {
// view.frame contains the frame for each item's view
// view.center contains the center of each item's view
}
}

iPhone's UITabBar as part of application window; problems when rotating device

I have an iPhone application that's using Navigation Controller to display the top bar (with title and back button, and such...).
I added a UITabBar to the application window, that enables to switch between the parts of it. Instead of adding the tab bar to each of ViewController's view I added the bar to app window.
(When I had it in the ViewController, switching between controllers made the tab bar to swipe left/right, when animated pop/push occured, together with whole view).
So, I added the UITabBar to the MainWindow.xib, and tied it to the app delegate's variable. In my didFinishLaunchingWithOptions method, I added the following code:
[self.window addSubview:navigationController.view];
CGRect frame = navigationController.view.frame;
frame.size.height -= tabbar.frame.size.height;
navigationController.view.frame = frame;
tabbar.selectedItem = [tabbar.items objectAtIndex:0];
to resize the main (navigationController's) view, in order to make the TabBar visible.
The problem shows up when I rotate the device -- my view gets stretched to full window and I loose the ability to show the TabBar.
I added a - (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation) fromInterfaceOrientation method to my ViewController, with the following code:
- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
CGRect frame = self.view.frame;
frame.size.height -= [AppState shared].tabBar.frame.size.height;
//frame.origin.y = [AppState shared].tabBar.frame.size.height;
//frame.origin.x = 100;
self.view.frame = frame;
frame = [AppState shared].tabBar.frame;
frame.origin.y = [UIScreen mainScreen].bounds.size.height - frame.origin.y - frame.size.height;
[AppState shared].tabBar.frame = frame;
}
It resizes the view, and moves the tab bar to up/down part of the view (I allow only Portrait/Portrait upside down orientations here). The problem is, my TabBar is turned upside down as well, and also, it's no longer clickable.
It looks like the image below:
Anyone knows how to handle this kind of situation? Or, how to make the tab bar not tied to view controller, but also able to handle interface's rotation smoothly?
You are using the tabbar in an unintended way. You seem to be using the UITabBarView as an uncontrolled element of other views. That is not it's function.
The UITabBarView should be controlled directly by a UITabBarController which in turn should be controlling all the view controllers for the views displayed in the tabbar i.e. the tabbar controller is a type of navigation controller that controls subcontrollers.
Suppose you have three tabs and the third one is a navigation controller. Your controller hierarchy would look like this:
TabbarController:
-->tab1ViewController
-->tab2ViewController
-->tab3ViewController(UINavigationController):
-->rootViewController-->secondViewController
You are trying to move and manage the tabbar view without its controller and the proper controller hierarchy. That isn't going to work.

Recommended Way To Go Full Screen

I want to have a UIView take over the whole screen to display an image from time to time, but most of the time I'm using a UISplitViewController for the main app functionality. Is there a recommended/best practice way to do this? Right now, I have a pointer to the main window and am just adding the UIView as a sub view and bringing it to front, but it won't display in the proper orientation. I was just wondering if there is a better way/something simple I'm missing.
Thanks.
I struggled with this same issue for some time and have come up with a clunky workaround. Before my solution, some more background on the obstacles:
In iPhone OS, the root view's coordinates are always in the literal fixed coordinates of the physical screen. "Up" is always device up. Subviews have to get coordinates which have been converted to a given orientation so that "Up" is what we expect to be: gravitational up.
Placing a UIView at the top of the view hierarchy (above the UIScrollView) would therefore require that you make these conversions yourself during orientation events. You get fullscreen, but you lose the benefit of the scrollView managing orientation coordinates for your subviews.
Keeping your view within the scrollView, there is no easy method as in Mac OS to simply collapse a pane of a splitView. Reviewing the header file reveals some private instance methods used to manage width.
So, I think that what you can do is maintain two "orientation-aware" view controllers (one splitView, one fullscreen), and switch them out and move your view between them when you toggle fullscreen. I did not go this route as it is obviously a touchy situation memory- and view-hierarchy- wise.
What I do is switch between an "almost fullscreen" width for the main view and regular split width. This works great except that the splitView's rounded corners are actually hard-coded images that get draw regardless of the splitView's dimensions. You will see these tiny black round corners 100% of the time. Here is the effect:
- (IBAction)toggleFullscreen:(id)sender; {
id appDelegate = [[UIApplication sharedApplication] delegate];
UISplitViewController *split = [appDelegate splitViewController];
//get master and detail view controller
UIViewController *master = [split.viewControllers objectAtIndex:0];
UIViewController *detail = [split.viewControllers objectAtIndex:1];
//In landscape permit fullscreen
if(self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
self.interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
CGRect f = detail.view.frame;
if ( f.origin.x == 0 ) { //exiting fullscreen
[sender setImage:[UIImage imageNamed:#"SlideLeft.png"]];
//adjust detail view
f.size.width = 703;
f.origin.x = 321;
[detail.view setFrame:f];
//adjust master view
f = master.view.frame;
f.size.width = 320;
f.origin.x = 0;
[master.view setFrame:f];
} else { //entering fullscreen
[sender setImage:[UIImage imageNamed:#"SlideRight.png"]];
//adjust detail view
f.size.width = 1024;
f.origin.x = 0;
[detail.view setFrame:f];
//adjust master view
f = master.view.frame;
f.size.width = 1;
f.origin.x = -1;
[master.view setFrame:f];
}
}
}
I got lots of this code from here and other sources, but as far as I know this implementation is unique. Probably because it is imperfect.
I ended up using a modal view controller which I present from the split view controller. I then specify the allowed orientations in -shouldAutorotateToInterfaceOrientation. I also hide the status bar. This works for my needs.