Putting a custom view into a UITableView - objective-c

I have a regular-style UITableView—the one that has a white background and gray horizontal lines to separate the rows.
I have another custom UIView that is just a 100x100 rectangle filled with redColor.
How can I put the latter into the former, such that it appears over the horizontal lines, but is still a “part” of the table view in the sense that when I scroll the table view around, the red view scrolls with it? In fact, I should also be able to put my finger on the red area and scroll the table view.
Once again, if the red view is placed to overlap some horizontal lines, it should appear over the lines. Sadly, when I just add the red view as a subview to the table view, the horizontal lines go over the red view; see this screenshot.
How can this be accomplished?

The correct place to handle the stacking order of your red square is in the layoutSubviews method. The table view sends itself layoutSubviews any time it adds or removes subviews (and at other times).
You need to make a subclass of UITableView that has a reference to the red square:
#interface MyTableView : UITableView
#property (weak, readonly) IBOutlet UIView *redSquare;
#end
You can initialize redSquare in whatever way you want. I just put it in my nib along with the table view, and moved it to be a subview of the table view in awakeFromNib:
#implementation MyTableView
#synthesize redSquare = _redSquare;
- (void)awakeFromNib {
[self addSubview:self.redSquare];
}
Anyway, to actually make sure the red square is always on top of the table cells and grid lines, override layoutSubviews like this:
- (void)layoutSubviews {
[super layoutSubviews];
[self bringSubviewToFront:self.redSquare];
}

EDIT
If you are trying to add the view a above the lines (hide the lines) try to use – bringSubviewToFront: to take it to the front of the table view.
[self.tableView bringSubviewToFront:redView];
ELSE
Add the view to the self.tableView.tableHeaderView this will place it above the table view and will scroll with the table view.
UIView *redView = [[UIView alloc]init];
redView.frame = //set the frame
redView.backgroundColor = [UIColor redColor];
self.tableView.tableHeaderView = redView;
Good Luck

Just add this view to UITableView as subview:
[tableView addSubview:myRedView];
See userInteractionEnabled property in order to handle interaction and scrolling.

Make your view a subview of any normal subview of the UITableView: a header, a footer or any UITableViewCell.

I think what you've described can best be achieved using a UITableViewCell or even a subview of the header. A cell has the inherent ability to scroll the table and can be customized any way you like it. It's essentially a view.
In your situation, for example, you may want the red box to appear by default at the top of the table. You would make the first cell a 'red box' cell, where you would insert your red box into the cell's content view.

So, your problem is basically, that the UITableViewCells of a UITableView are added as Subviews dynamically, and you cannot control wether they are added in front of or behind your view.
So to keep your view at the front, you need to get it back there every time cells may be added, which occurs when the UITableView scrolls.
I would suggest you try adding your custom view as a subview and then override -scrollViewDidScroll like so:
- (void)scrollViewDidScroll {
[super scrollViewDidScroll];
// myCustomView is your custom view referenced in an IVar
[self.tableView bringSubviewToFront:myCustomView];
}

Edit your viewWillAppear delegate with these lines
UIView *redView=[[UIView alloc]initWithFrame:CGRectMake(30,30, 100, 100)];
redView.backgroundColor=[UIColor redColor];
[self.tableView addSubview:redView];

Related

UISearchBar Disappears After Events

I have a UISearchBar in my view contained inside a UITableView above the top-most cell. Everything works fine until I push a new view controller onto the navigation stack. Once I pop the new VC and return to the original view controller the search bar does not show atop the table. Instead there is a white space between my navigation bar and the top row of my table view. However, if I click this white space then the search bar opens as if it were there normally, and when I click cancel again all is well.
I think that somehow the view for the UISearchBar is being set to be entirely white, but I have no idea why. I originally have the UISearchBar just added into the storyboard, as part of a UISearchDisplayController. How can I prevent the search bar from going white after my VC is unwound to from another controller than was pushed onto the navigation stack?
I have the same problem. I don't think it relates to the color of the search bar but to the layout of the search bar's subviews. After many trials and errors I came up with this solution:
#interface MySearchBar : UISearchBar
#end
#implementation MySearchBar
-(void) layoutSubviews
{
[super layoutSubviews];
if (self.subviews.count == 1)
{
UIView *subview = [self.subviews objectAtIndex:0];
subview.frame = self.bounds;
}
}
#end
Use MySearchBar instead of UISearchBar

How can I create a ViewController which has a fixed MapKit above a scrolling TableView?

How can I create a ViewController which has a fixed MapKit above a scrolling TableView?
I am coding this using Storyboards, and while I am an experienced developer this is my first time using iOS, XCode and Objective-C. I currently have a method which kind of works, but the method which I am currently using works by setting the MapKit as the section header of the table view inside a UITableViewController and does not look right. (It works since as I have only one section that MapKit section will always be fixed while I scroll the table view.)
The code that does this looks like this:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 200.0;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
MKMapView *mapView = [[MKMapView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 200)];
return mapView;
}
Unfortunately doing this means that the scrolling bar appears alongside the Map as well as the table view. Instead, I wish the scrolling bars to only be as tall as the table view.
There are other answers around the site about doing stuff like this, but none of them mention the height of the scroll bars (and the elegance of the code.)
Any ideas?
The link you provided is exactly what you should do, but in the Xib file also include the MKMapView above the table view, and set the outlets properly for that as well(And implementing the proper delegate protocols). The scroll bar will only go as high as the table view because it's a subview of the actual table, and, generally, this is the accepted way of doing things on the UI unless you want to do absolutely everything programmatically.
In this case you can take a separate UIView and place it after Exit. Put your mapView inside that UIView and do the required. Also draw an outlet for your tableView(myTableView) as well as the UIView(viewWithMapView). Now add this in your viewDidLoad() :-
[_myTableView setTableHeaderView:_viewWithMapView];
This will add your mapView as tableView header instead of section view header.

Set All UILabels Hidden

Is there a way to set all UILabels as hidden in Objective-C? I'm showing and hiding labels based on if statements and feel like I'm writing really bulky code. Is there a way to select all UILabels to setHidden:YES a la CSS?
Edit: I need one of them visible at a time, not all hidden at once.
Thanks!
If all your labels lay at the same view you can use it's subviews property:
for (UIView *subview in self.view.subviews) {
if ([subview isKindOfClass:[UILabel class]]) {
subview.hidden = YES;
}
}
And if there are numerous of views with labels you can even add a category to the whole UIView.
#interface UIView (HideLabels)
- (void)hideAllLabels:(BOOL)hide withExcludedLabel:(UILabel *)label;
#end
#implementation UIView (HideLabels)
- (void)hideAllLabels:(BOOL)hide withExcludedLabel:(UILabel *)label
{
for (UIView *subview in self.view.subviews) {
if (subview != label && [subview isKindOfClass:[UILabel class]]) {
subview.hidden = YES;
}
}
}
#end
There's no other way to do this.
Edit: the code above updated according to your needs.
If you only need 1 UILabel at all time, you can reuse the same UILabel. The advantage is you use a bit less memory and you don't need to manage all the UILabels. The disadvantage is that you need to recalculate/store the coordinates to put the UILabel and stores the content of the UILabel (the management is shifted to this).
Now that the requirement has changed, the below answer is no longer valid. However, I still keep it there, in case anyone wants to hide/show all labels.
I don't think you can do it like CSS, but we can use a trick to avoid having to loop through all the UILabels to setHidden.
You can put all the UILabels as subview of a transparent UIView. The size and origin of the transparent UIView should be configured so that the coordinate is the same as when you don't use the transparent view (to avoid confusion). When you want to hide all UILabels, you can just hide the whole transparent UIView.
This has a drawback is that all the UILabels must be on top or under the existing view. This means that you cannot freely adjust some label to be on top of certain element, and some label to be below certain element on the existing view. You need to create another view for that purpose, and things will get quite messy there.

UINavigationBar to a higher xib layer index

Similar to how z-index works in HTML, I want to make my UINavigationBar be on the highest layer so that everything in my scroll view goes underneath it.
I currently just have a xib with a UINavigationBar located at the top with a UIScrollView below it vertically that has Bounce Vertically checked. When you slide the scroll view the content from the scroll view will appear to be above the UINavigationBar and covers it.
You can set the zPosition of navigationBar to the topmost layer as below
navigationController.navigationBar.layer.zPosition =[[self.view subviews] count];
If you use a UIViewController I would suggest using this code :
self.navigationController.navigationBarHidden = false;
But if you are using a plain UIView I will refer you to the UIView documentation http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/doc/c_ref/UIView
Look for the #property(nonatomic, readonly, copy) NSArray *subviews and other subviews related method.
You can order your view relative to each other with those method.

Adding a UINavigationController as a subview of UIView

I'm trying to display a UILabel on top of a UINavigationController. The problem is that when I add the UILabel as a subview of UIWindow it will not automatically rotate since it is not a subview of UIViewController (UIViewController automatically handles updating subviews during rotations).
This is the hierarchy I was using:
UIWindow
UILabel
UINavigationController
So I was thinking I could use the following hierarchy:
UIWindow
UIViewController
UIView
UILabel
UINavigationController
This way the label could be displayed on top of the UINavigationController's bar while also automatically being rotated since it is a subview of UIViewController.
The problem is that when I try adding a UINavigationController as a subview of a view:
[myViewController.view addSubview:myNavigationController.view];
it will appear 20 pixels downwards. Which I'm guessing is because it thinks it needs to make room for the status bar. But, since the UINavigationController is being placed inside a UIView which does not overlay on top of the status bar, it is incorrectly adding an additional 20 pixels. In other words, the top of the UINavigationBar is at the screen's 40 pixel mark instead of at 20 pixels.
Is there any easy way to just shift the UINavigationController and all of its elements (e.g. navigation bar, tool bar, root view controller) up 20 pixels? Or to let it know that it shouldn't compensate for a status bar?
If not, I guess I would need to use my first hierarchy mentioned above and figure out how to rotate the label so it is consistent with the navigation bar's rotation. Where can I find more information on how to do this?
Note: by "displaying a label on top of the navigation bar", I mean it should overlay on top of the navigation bar... it can't simply be wrapped in a bar button item and placed as one of the items of the navigation bar.
Using this code seems to work:
nav.view.frame = CGRectMake(nav.view.frame.origin.x, nav.view.frame.origin.y - 20,
nav.view.frame.size.width, nav.view.frame.size.height);
I did this before adding the navigation controller as a subview. Using the [UIApplication sharedApplication].statusBarFrame instead of the hard coded 20 would probably be a good idea too.
I'm not sure if it's the best way to do it though.
If you want a frame representing the available content area, then you should just use: [[UIScreen mainScreen] applicationFrame]. Of course, this restricts your top-level view controller so that it can only be top level. So still kind of dodgy, but less so.
Why don't you use App Frame instead of adding some values to origins? I mean using:
CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
as a reference frame, and do something like this:
nav.view.frame = CGRectMake(appFrame.origin.x, appFrame.origin.y, ...
This one worked for me.
I had this same problem actually but managed to fix it.
I noticed that my view controller's view had the correct frame, but the view controller's navigation bar did not (it had a frame origin of (0,20) ).
Insert this into the view's controller that is the superview of the navigation controller:
- (void) viewDidAppear:(BOOL)animated {
if (navigationController.navigationBar.frame.origin.y != 0) {
[[navigationController view] removeFromSuperview];
[[self view] addSubview:navigationController.view];
}
}
Swift 5:
add the following line in the viewDidLoad() of the root view controller of the UINavigationController.
self.edgesForExtendedLayout = [.top, .bottom]