How do I add a UINavigationBar completely programmatically? - ios

I'm trying to figure out, given a UIViewController subclass, how to add a UINavigationBar to it. All the questions and answers seem to be either embed it in a navigation controller (not possible in this case) or via Storyboard, but I need to do it completely in code.
Do I just add it as a subview of the view controller's view? Will it become topLayoutGuide after I pin it to the topLayoutGuide, which would be the status bar prior to adding it? Or am I supposed to be setting an attribute on the view controller I cannot figure out, instead of adding it?

Your on the right track, you just add as a subview like you would any other view. The trick to the status bar is setting yourself as the navigationBar delegate and returning UIBarPositioningTopAttached in the delegate method
.....
self.customNavigationBar.delegate = self;
[self.addSubview self.navigationBar];
....
- (UIBarPosition)positionForBar:(id<UIBarPositioning>)bar {
return UIBarPositioningTopAttached;
}

Related

Objective-C - UISearchController results overlap on UISearchBar when scrolling

My goal is the following: have a UISearchBar which is always visible. When it isn't active, there is some content on the bottom part of the controller. When it becomes active, I want to display the results of the search in a UITableView that overlaps the UIController.
The way my search is setup is the following:
I have a fixed UIView on the top of my ViewController, created via Storyboard.
In my viewDidLoad method, I add the searchController.searchBar to that UIView, so that the UISearchBar is permanently active.
I use the very useful following line to hide/display the searchResultsController:
searchController.searchResultsController.view.hidden = FALSE;
All of this works perfectly, except that when I display results, if I scroll down, the results are displayed on top of the UISearchBar.
What is the way to avoid this? I believe this is due to the fact that I use the following line:
[searchBarView addSubview:self.searchController.searchBar];
where searchBarView is an empty placeholder view which I create on my Storyboard and stick to the top. This is the only way I found that have the searchBar displayed permanently. It's important to note that I don't use a navigation controller and that's why I don't add the searchController as a navigation item.
Thanks a lot for your help!
The way in which you are using UISearchController is totally wrong. In case of searchcontroller, you must provide searchcontroller as result tableview's tableHeaderView. Here you are adding searchcontroller into UIView and tableview at some other place. This is obviously not going to work.
Still you can try this possible ways ---
Make the topView an opaque.(dont provide transparency to that containerview)
Use UISearchbar instead of using UISearchcontroller.
By this way, i think your problem will get solved.
Don't use translucent navigation bar
self.navigationController.navigationBar.translucent = NO;
I have search this for many hours and final result was to put this line in viewDidLoad:
self.extendedLayoutIncludesOpaqueBars = YES;
Problem solved :)
I hope it will working for you. Thanks.

Put a view on Storyboard above all others

I have a ViewController file and in this ViewController file, I create programmatically a view which it takes all the available place on the window.
Now, I want to make a view with the Storyboard. I put this view (in my case a button) on my Storyboard.
And when I launch my application, I cannot see my button because it is under the view which I created before programmatically.
Is this possible to have my Storyboard view above the other view ?
Thanks a lot !
Yes, it's possible.
Looks like you use addSubview method.
The problem that you encountered is that views added in such way overlay the existed views.
You can either bring button to front with bringSubviewToFront or use insertSubview:belowSubview and insertSubview:aboveSubview to fix such issues. In both cases you'll need your button stored in property.
It sort of depends on your view hierarchy setup.
I'm assuming this:
A - View controller view
| B - Your button
| C - Programmatically added fullscreen view
In the above case you could do something like this:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view bringSubviewToFront:self.button];
}
Point is bringSubviewToFront: moves a subview to the top of the hierarchy.
So you will have to create an outlet for the button (connect the button in storyboard to the name given to it in your .swift file) Once you have added the view programmatically, you just have to do
self.view.bringSubviewToFront(button)
Assuming that button is the name of the outlet. This line will be added after adding the view programatically.

How do I make my iOS7 UITableViewController NOT appear under the top status bar?

My root controller is a TabBarController (tabbed application). One of the tabs, is a UITableViewController. When I switch to that and scroll through the items, they show up under the status bar at the top (signal, battery, etc). I don't want that. I want that to be opaque or... something. It's visually jarring with the text of the table cells underlapping the status stuff.
Can I fix this from my Storyboard with some attributes setting that I don't understand? Or do I need to add some methods to my subclasses? Or maybe I need to wrap my UITableViewController with some other kind of controller?
I've tried numerous variations of the ViewController Layout and Extend Edges settings in the Storyboard attributes page, but none of them seem to change it for the better.
Update: I think my problem is very similar to iOS 7: UITableView shows under status bar. The only difference, is that I'm embedded in a TabBarController, and that case is as the root view. I tried the solution listed there of embedding in a NavigationController and setting Show Navigation Bar to False, but it didn't make any difference.
Screen Shots:
My storyboard (shrunk) showing a tabbed controller, with 2 children, one single view, and the other the table view.
Settings for the tab bar controller
Settings for the table view controller
What the app ends up looking like on my phone
How the Story Ended
Despite lots of answers below, none of them really worked. Some kind of a little, but not really. I tried the Embed in NavigationController approach as well, and that also had issues. What did work though, was this:
Add UIViewController
Set child controller relationship with it and tab bar controller (just like the other two I already had)
Add a TableView (not controller) to the new UIViewController, position as desired, it'll snap to the bottom of the status bar
Set the TableView's delegate and tableSource as the new controller
Create a custom UIViewController subclass and update the class type of the controller in the storyboard
Copy the table related methods from my custom UITableViewController subclass to my new subclass
Select my prototype table cell from the original, and command+drag it to the new table view
Happily delete the original TableViewController (and wrapper NavigationController) too
Update the tab bar item to match the previous
Chock another one up for "you're trying to hard"
Try this in viewDidLoad:
self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0);
20 px being the height of the status bar. If you have a navigation bar use 64 instead of 20
In case anyone is still reading this thread:
What worked for me is to also uncheck the "Extend Edges" options in the parent tab bar controller. That gives a consistent behaviour in iOS7 as well as iOS6.
I was having the same problem when using the SWRevealController.
Using the comments above and below I was able to get it to work by putting this in the
-(void)viewWillAppear instead of ViewDidLoad
self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0);
Have you tried adding something like this to the view controller's viewWillAppear method:
if ([self respondsToSelector:#selector(setEdgesForExtendedLayout:)])
{
self.edgesForExtendedLayout = UIRectEdgeNone;
}
In case anyone misses the How the story ended section at the end of the (now long) question, the short answer is: Use a simple UIViewController with a TableView, instead of a TableViewController if you want to achieve the stated goal.
I added the following to viewWillAppear
self.tableView.contentInset = UIEdgeInsetsMake(22, 0, 0, 0);
You can also solve this through Storyboard.
Select the Table View from the Project Outline (left side of the editor) and then go to Properties (right side) > Size inspector tab > Scroll View > Content Insets > Top
suppose your table's #IBOutlet is
#IBOutlet var tableView: UITableView!
create a function prepareTable(). add the below line into that method
tableView.contentInset.top = UIApplication.shared.statusBarFrame.height
call this method from ViewDidLoad()
Looks like you just want to make the NavBar nonTranslucent, you could try using
[self.navigationController.navigationBar setTranslucent:NO];
I encountered the same problem and the solution that worked for me was to add a section header view:
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
I then added 20 to the origin.y of my text on the header.
I then changed the header height by adding 20 to the original height using the
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return myHeight+20;
}
For me an easy solution was (code is in C# that's because appears to be invalid)...
Just in the UITableViewController constructor create a new UIView (set same dimension of the TableView).
Add the TableView to the new created view.
Set new created View to the UITableViewController's View property...
On the constructor of the UITableViewController
var view = new UIView ();
view.Frame = this.TableView.Frame;
view.AddSubview (this.TableView);
this.View = view;
As of iOS 8.4 I didn't get anywhere with the various storyboard options listed in other answers.
I worked around this without resorting to any dimensional constants by putting a regular view in my tab controller, then filling that with a "Container View" which I then connected to my UITableViewController with an "embed" segue.
My table now respects both my navigation bar and the tab bar at the bottom.
Let's say your Outline view, or Storyboard, is a Tab Bar Controller that currently has a Table View Controller. When you run the app, your table view cells are running under the status bar that seems to have a transparent background. This sucks!
If you spent hours trying everything you could find on StackOverflow to no avail, felt like maybe it really was time to consider a new career and were preparing to dial 1-800-LUV-TRUK or 1-800-SHT-KIKR, give yourself a pat on the back, open up whatever elixir you drink in times such as this, because it's not your fault. Really. Travis is absolutely right that no amount of code in viewDidLoad, viewWillAppear, or viewDidAppear or button selecting/deselecting in IB will help in this situation.
Travis' solution above will certainly work, but it's a bit long, there's copying and pasting of code, I have a short attention span such that an episode of Bugs Bunny feels like a full-length movie to me, so I just know that I'll screw-up anything that complicated. But, hey, your mileage may, nay likely will, vary. Anyhoo...
In Xcode 7.3 for an app running iOS 9 (I assume 7 and 8 but I don't know this for certain and am currently too lazy to check) there is an easier way that doesn't require one to write any code. And it's done all within Xcode's Interface Builder.
(Caveat: Sorry if any of the terms aren't accurate. Please let me know where I was mistaken and I'll correct any mislabeling.)
Go to the Utilities area of Interface Builder and select the Object library from the library pane.
Select a Navigation View Controller object and drag it into your Storyboard scene. You'll notice that two scene items appear while you're dragging–these are a Navigation Controller Scene and a Table View Controller Scene.
Delete the duplicate Table View Controller Scene that came-along with your Navigation Controller Scene.
Select the relationship connection between your Tab Bar Controller and your Table View Controller and hit "Delete".
Reposition the Navigation Controller and your Table View Controller the way you want in your storyboard.
Control-drag from your Tab Bar Controller Scene to the Navigation Controller Scene and select "Relationship Segue, view controller".
Control-drag from your Navigation Controller Scene to your Table View Controller Scene and select "Relationship Segue, root view controller".
Lastly, in the Utilities' Objects library, drag a Navigation Item object into your Table View Controller Scene's Table View.
Now when you run your app, you will have a navigation bar and your table view cells will no longer appear under a transparent status bar.
Hope this helps.

Why can't I assign a UIView to self.view?

This might be a stupid question, but I'll shoot.
I made a little test project to test out a concept I had for a sliding view controller type of thing. I naively assumed I could create a UIView (let's call it peekView) with an outlet in a controller, and call something like [slidingControllerSlideFrom:self.view] from any visible view controller, the implementation of such being:
- (void)slidingControllerSlideFrom(UIView*)controllersMainView
{
// push side controller to top of navigation stack
self.peekView = controllersMainView;
// sliding animation
}
But there is no effect. No crash, no warning, no change of view in the pushed controller.
Of course, the pushed controller crashes when trying to add self's view as a subview, but assigning it to a predefined UIView just results in nothing.
So, why? And if a mere 'why' is not enough of a question- what happens when I try to assign one controller's view another controller's subview, and what was the reason for designing UIKit where you cannot set views from self.view?
To do that you have two options:
1 - If the controller in the peekView is always the same one in a given scene, use a "Container View". Those are explained here. Basically, they allow you to add a view in your scene that is managed by another controller.
2 - If the controller in the peekView depends on different conditions, you will have to create something similar to a custom tabbarcontroller. That means that you instantiate the controller that you need, add it's view as a subview of peekView (not assign the controller's view to the peekView itself) and then use didmovetoparentviewcontroller to notify the child controller. This question might help.
UPDATE:
Just saw your comment, so let me answer what you actually asked: The peekview property is actually just a reference to the real UIView you placed in the screen. When you do this:
self.peekView = controllersMainView;
You are changing the reference, but no the view object itself. That's why you are not seeing any changes. There are ways of adding a new view to the controller from code, but it is much simpler to simply use addSubview to add your controllers view to a UIView that is already in the controller.
Check out the discussion here: subView Slide up from bottom of the screen
and here: SubView slide in animation, iphone
Hopefully that gives you a bit of framework on how to approach this task!

Is there any way to place a UIView above a UIPopoverController?

I have code in my app that adds a UIView to the root view controller's view. This view is semi-transparent, and functions as a modal overlay that covers the entire application (the view contains a button that when clicked removes the view from its superview, which is how the user gets back to the main application).
This has worked fine up to now, but now we're using UIPopoverControllers and attempting to use this same modal overlay to block the main application. The problem is that this UIView is shown behind the popover, instead of on top of it.
Is there any way to add a subview to a root view controller's view in such a way that it appears on top of any visible UIPopoverControllers, but without dismissing them?
The reference on UIPopoverController states:
"The popover content is layered on top of your existing content in a special type of window."
NSLogging of the subviews-array shows, that the view is not added to the view hierarchy of the viewController that displays it. From my point of view what you are trying to do isn't possible.
Here is an idea though:
You could add your blocking UIView to the contentViewController of the UIPopOverController and use it to set a property on the contentViewController.
Then you set your main viewController, which actually displays the UIPopoverController, as delegate of the popover and do something like this:
- (void) popoverControllerShouldDismissPopover:(UIPopoverController*)popoverController
{
if(popoverController.contentViewController.yourProperty)
{
return YES;
}
return NO;
}
I don't know how much this helps in your current situation, but maybe it gets you started.

Resources