iOS - Why setting and displaying subviews in initWithNibName doesn't work? - ios

I have a simple xib file which its layout is:
Which the pale green UIView is referenced to an IBOutlet view_content in my UIViewController class. Then I did the following in initWithNibName:bundle: of my ViewController.m:
self = [super initWithNibName:nibNameOrNil bundle:nil];
if (self) {
self.view_a = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
self.view_a.backgroundColor = [UIColor purpleColor];
[self.view_content addSubview:self.view_a];
self.view_b = [[UIView alloc] initWithFrame:CGRectMake(150, 150, 100, 100)];
self.view_b.backgroundColor = [UIColor blueColor];
[self.view_content addSubview:self.view_b];
self.view_c = [[UIView alloc] initWithFrame:CGRectMake(200, 200, 100, 100)];
self.view_c.backgroundColor = [UIColor greenColor];
[self.view_content addSubview:self.view_c];
}
return self;
I expected there will be 3 small squares, each with different colors as declared in the code above, but they did not show up which the view is loaded and showed. But instead if I move the addSubView: calls into viewDidLoad, they are displayed as expected. I tried to test if view_content is nil in the if-segment and seems it is nil at that time. I don't understands why this is happening and how it works, isn't the view and its components are supposed to be loaded from the xib?
Thanks for any help!

At the point initWithNibName:... is called, the view itself is not yet loaded. This is an optimization (lazy loading) that defers loading the view until it is actually needed. The view is actually loaded when viewController.view is first-time accessed, and that's when viewDidLoad is called.
Some people force the view controler to load its view by calling [viewController view] (or [self view] if it's within the view controller's class).

The reason is simple. View is not loaded at that time when you are initializing the controller means initWithNibName. So best place is viewDidLoad where your all outlet has been assigned.

Related

iOS - Background view is consuming touch events

I'll start by describing how the screen looks. It is a UIPageViewController that covers 100% of the screen and a UIView (on top of the page vc) that covers 20% of the screen. Easy, right? basically UIView is over UIPageViewController.
So, I'm adding said UIPageViewController programatically to my view controller with self.view.addSubview(pageVC.view). but the UIView is defined in the interface builder
For this reason, since the UIPageViewController is added last it covers the UIView which forced me to call myUIView.layer.zPosition = 1 so that the UIView is visible.
The problem is any touch made on the UIView is instead consumed by the UIPageViewController. how can I solve this?
The layer property of a particular view is only used for the rendering of that specific view. That's the reason why changes to the layer won't impact anything regarding the interaction with that specific view.
However, the -sendSubviewToBack: and -bringSubviewToFront: methods on UIView were designed to achieve exactly what you are looking for. Here you can read the docs for them.
I wrote this piece of code so that you can test what this method is doing. Try to comment/uncomment the last line or changing the order in which the subviews are added to get a better understanding of how it works.
UIView *firstView = [[UIView alloc] initWithFrame:CGRectMake(200, 200, 100, 100)];
UIView *secondView = [[UIView alloc] initWithFrame:CGRectMake(200, 200, 150, 150)];
firstView.backgroundColor = [UIColor greenColor];
secondView.backgroundColor = [UIColor redColor];
[self.view addSubview:firstView];
[self.view addSubview:secondView];
UITapGestureRecognizer *firstTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(firstTap)];
UITapGestureRecognizer *secondTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(secondTap)];
[firstView addGestureRecognizer:firstTap];
[secondView addGestureRecognizer:secondTap];
[self.view sendSubviewToBack:secondView];

SVPullToRefresh custom view causes layoutIfNeeded go into infinite loop

I tried using custom views for the SVPullToRefresh but I've stumbled upon weird issue. I've made the demo project to illustrate the issue: https://github.com/gaks/SVRefreshProblem
The project is simply one UITableViewController with a TableView and the xib file with custom refresh bar view. The SVPullToRefresh is added to the tableView via the IBOutlet in the viewDidLoad: https://github.com/gaks/SVRefreshProblem/blob/master/SVRefreshProblem/DemoViewController.m
There is also a code that loads the RefreshBarView from the xib file and sets it as a custom view for SVPullToRefresh (for 'loading' state only for illustration):
RefreshBarView* refreshBarView = [[[NSBundle mainBundle] loadNibNamed:#"RefreshBarView" owner:self options:nil] objectAtIndex:0];
refreshBarView.bounds = CGRectMake(0, 0, 320, 60);
[[tableView pullToRefreshView] setCustomView:refreshBarView forState:SVPullToRefreshStateLoading];
The issue is that when you pull the table view to trigger the refresh it goes into infinite loop and eat-all-available-memory state after calling [self layoutIfNeeded] in the - (void)setState:(SVPullToRefreshState)newState. As far as I was able to debug it keeps calling - (void)layoutSubviews over and over.
What's weird is that when you open the xib file: https://github.com/gaks/SVRefreshProblem/blob/master/SVRefreshProblem/SVPullToRefresh/RefreshBarView.xib
... and remove the label from the view - it works just fine (except you have an empty view).
What am I doing wrong?
Loading from nibs is for ViewControllers, not views proper. You can try to do this programmatically which will work :
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 60)];
[v setBackgroundColor:[UIColor grayColor]];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 60)];
[label setText:#"asdf"];
[v addSubview:label];
[[tableView pullToRefreshView] setCustomView:v forState:SVPullToRefreshStateLoading];
Try to turn off "Use Auto Layout" in your nib.

Does removefromsuperview remove subviews?

If I have nested subviews, will all subviews get disposed if i call removefromsuperview?
Pseudocode:
UIView *viewA = [[UIView alloc] initWithFrame:CGRectMake(0 , 0, 100, 100)];
[self.view addSubview:viewA];
UIView *viewB = [[UIView alloc] initWithFrame:CGRectMake(25 , 25, 50, 50)];
[viewA addSubview:viewB];
UIButton *buttonC = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[viewB addSubview:buttonC];
And then buttonC is pressed:
[viewA removeFromSuperView];
All views are removed from the screen, but are they removed properly?
Do I need to remove all views manually?
All views will be removed. If you maintain a strong reference to viewA then all of the views will still be there and can be added again later. If you don't, they will all be destroyed.
As long as you have no other references to your views A..C, they will be removed and destroyed
You can check it easy. Create subclass of uiview, overide it dealloc method and set brakepoint there. Than create instance of this class and add it to your view as subview.
When you`ll call removeFromSuperview in your view brakepoint will be activated.
That`s it.

Replacing self.view with new UIView shows black view

I want to change the existing view in a UIViewController to a new view. The new view contains the old view and a little banner view.
Doing this fairly simple change leaves me with a black view.
My code looks like this
UIView *existingView = self.view;
UIView *newView = [[UIView alloc] initWithFrame:existingView.frame];
UIView *bannerView = [[UIView alloc] initWithFrame:CGRectMake(0, (self.view.frame.size.height - 50), 320, 50)];
CGRect existingViewFrame = existingView.frame;
existingViewFrame.size.height -= 50;
existingView.frame = existingViewFrame;
[newView addSubview:existingView];
[newView addSubview:bannerView];
self.view = newView;
However when switch Tabs and come back to the view which changed the view is shown just like I want. I guess I need to set a flag or something to tell the controller to redraw it's (new) view.
Edit
I wrote an simple example for this problem. You can find it on GitHub: https://github.com/Oemera/ChangeView
You did not say where you do this. It may be that you need to save the original view's super view, then add the new view to that views subViews array. I'm betting that is the problem.

Can't add UITableView as subview

Xcode 4.3/iOS 5.1/Storyboards.
I have a UIViewController with a UITableView in the storyboard
when initializing that view
-(void) loadView {
UITableView *tTableView = [[UITableView alloc] initWithFrame:CGRectMake(20,60, 300, 300)
style:UITableViewStyleGrouped];
[tTableView setDelegate:self];
[tTableView setDataSource:self];
[tTableView setBackgroundView:[[UIView alloc] initWithFrame:self.tableView.bounds]];
self.tableView = tTableView;
self.view = tTableView; // <--- THIS WORKS
}
This works, but the TableView is stretched over the entire width of the screen. I'd like it to be of a certain size as I'm using it for a login username/password control.
I was told to add it as a sub-view, but this crashes my app with some kind of recursive calling into the same "loadView" method.
-(void) loadView {
UITableView *tTableView = [[UITableView alloc] initWithFrame:CGRectMake(20,60, 300, 300)
style:UITableViewStyleGrouped];
[tTableView setDelegate:self];
[tTableView setDataSource:self];
[tTableView setBackgroundView:[[UIView alloc] initWithFrame:self.tableView.bounds]];
self.tableView = tTableView;
[self.view. addSubview: tTableView]; // <-- THIS CRASHES
}
Why is this crashing? How can I add the UITableView to a subview where I can control its width and not have it occupy the entire width of my screen?
you need to self.view = tTableView or self.view = [UIView alloc] initWithFrame.... and then add the table view. Overriding the loadView method means you are going to create the self.view. So for your case, create an UIView, set it to self.view and then add your tableView as subview of self.view
You use the loadView method, which means you define the view by yourself.
So self.view is not defined and the controller complains that its view is not defined when it should display itself.
Either put your interface in a xib (or storyboard) and bind those values in viewDidLoad, or instanciated the self.view by hand in loadView.

Resources