View placeholders replacement with xib using the awakeAfterUsingCoder method - ios

I am currently working on an iOS app that has multiple xibs containing a UIView subclass which is acting as a placeholder for the header of the screen.
When the view controllers are loaded from their nib, they instantiate the HeaderView placeholder. In the HeaderView class I have a method that replace the placeholder view by the real one, here is the code :
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
if (![[self subviews] count])
{
if (headerViewNib == nil)
headerViewNib = [UINib frameworkNibWithNibName: #"HeaderView"];
HeaderView * headerView = [[headerViewNib instantiateWithOwner: controller options: nil] lastObject];
[headerView setTranslatesAutoresizingMaskIntoConstraints: NO];
return headerView;
}
return self;
}
Until now, everything works correctly. But now I need to add a menu similar to a popover to the header. So I added a UIButton to the HeaderView xib, as expected it is showing in all the view controllers, but now I need to manage the touch on that button.
I added a "- (IBAction)showHeaderMenu" method to my base UIViewController class, and I configured in the HeaderView.xib the file's owner to this class, so I could connect the button touch action, to the showHeaderMenu action.
The problem is that I am passing "controller" as owner to the instantiateWithOwner:options: method. But this controller is nil. So my touch events on the menu button are not handled.
So now I am searching a way to get the "controller" property set correctly. At the moment in all my view controllers xib, I connected the placeholder headerview "controller" outlet to the file's owner (which is the controller), but it does not seems to be working.
If you have a different pattern to suggest, that does not force me to drop my HeaderView.xib or the placeholders view, i'm open :)

I'm not sure i get exactly your problem but if it is related to your not being able to set up the IBAction through IB because something is not loaded or whatever you can always setup the IBAction event listener through code.
UIButton *yourButton = [[UIButton alloc] init];
[yourButton addTarget:self action:#selector(myMethod:) forControlEvents:UIControlEventTouchUpInside]

Related

Access contained UIView component from Parent ViewController

In my parent viewcontroller, I have a view container, I added a subview to it:
m_cardDetail = [[CardDetailView alloc] init];
[_m_viewContainer addSubview:m_cardDetail];
[m_cardDetail initialize];
But how can I let my parent viewcontroller process a button click (button resides in m_cardDetail subview). I tried setting the button tag property of the button to 1010 and using this code in parent viewcontroller:
UIButton *aButtonView = (UIButton *)[m_cardDetail viewWithTag:1010];
[aButtonView addTarget:self action:#selector(aButtonTapped:) forControlEvent:UIControlEventTouchUpInside];
and also added this in viewcontroller:
- (void) aButtonTapped:(UIButton *) sender {
//Do something
}
but im getting the Unrecognized selector error
You should treat another view controller's views as private, even one that is a child view controller. As #Ostanik suggests in his answer (voted), setting up a protocol and a delegate is a good way to make a connection between a parent and child.
Note that an embed segue is a very clean way to set up the parent/child link. You simply create a container view in IB and control-drag from the container view to the scene of the view controller that you want to be a child, and Xcode does the rest. When the parent view controller is loaded the embed segue is invoked and you can set up the delegate in your prepareForSegue method.

Using custom View with Storyboard without adding it twice

I'm using Storyboard and trying to reuse a View for different ViewControllers.
For that, I created a custom .xib (MyCustomView.xib) and class (MyCustomView) as suggested pretty much everywhere.
In my Storyboard I set the custom view to be of type MyCustomView.
In MyCustomView.xib, set First Responder to MyCustomView.
In MyCustomView.m, I added the following method:
-(id)initWithCoder:(NSCoder *)aDecoder{
if ((self = [super initWithCoder:aDecoder])){
UIView *myCustomView = [[[NSBundle mainBundle] loadNibNamed:#"MyCustomView" owner:self options:nil] objectAtIndex:0];
[self addSubview: myCustomView];
}
return self;
}
Problem is, [self addSubview: myCustomView] adds a new MyCustomView to existing MyCustomView, so the view is added twice.
How can I get rid of one of the two?
EDIT
My question is not really clear, so I thought some screen caps would help.
Here is my Storyboard with a custom view. Custom class is set to MyCustomView.
(I also added a grey background and a label for testing purpose only)
Now, in MyCustomView.xib, I set File's owner to be of Class MyCustomView:
And add outlets for title Label and imageView:
With the initWithCoder method as written above, this works fine, except when I debug I can see this:
So self is of course of type MyCustomView, as chosen in the Storyboard, but it contains 2 subviews :
First is the test label from my storyboard
Second is the view from MyCustomView.xib, itself containing a Label and image view. That's this view I want to get rid of.
What is self ?
Here, it is the MyCustomView you just instantiated. So, you're adding a new UIView (not MyCustomView) to your new MyCustomView instance created by the initWithCoder method.
initWithCoder is the method called when loading your storyboard view.
If you want to instantiate a new MyCustomView in your MyCustomViewController you have to call
MyCustomView *newInstance = [[MyCustomView alloc] init];
[self addSubview:newInstance];
Where self is the instance of MyCustomViewController in your storyboard.
EDIT
OK, I understand better your question.
If you want to load the MyCustomView nib file, you don't need to create a class for it (except if you have specific variables you want to access in it.
Just do what you're doing here, but in the view controller, which will be the self. :)
You might want to have a look at : How to load a UIView using a nib file created with Interface Builder
EDIT 2
Ok, I think I get it :
delete the MyCustomView in your Storyboard.
create a class MyView or whatever which you attach to the view named "View" in your Storyboard.
in the init method of your class you instantiate your MyCustomView nib
add the resulting UIView to the views stack like you did it.
self will be MyView (present in the Storyboard), MyCustomView won't appear in the storyboard but created programmatically in the init method of MyView.h
You will have to add constraints programmatically. Here is a post that can be helpful: Adding View Programatically With Auto Layout Gives 'NSGenericException', reason: 'Unable to install constraint on view

addTarget for another class

I have a UIView that has some buttons that I need to add actions to.
I've tried the this:
HeaderViewController * header = [[HeaderViewController alloc]initWithNibName:#"HeaderViewController" bundle:[NSBundle mainBundle]];
[header.aboutUs addTarget:self action:#selector(aboutUsPage:) forControlEvents:UIControlEventTouchUpInside];
[header.MyLibrary addTarget:self action:#selector(myLibraryPage:) forControlEvents:UIControlEventTouchUpInside];
[self.tableView addParallaxWithView:header.view andHeight:229];
But the button is not responding at all.
What have I done wrong.
Here's the thing, You are creating the view controller and then setting targets and actions for buttons that are referenced as the view controller's property.
I suspect that if you log or put a breakpoint just before you set the target and actions and then step through, the buttons will be nil.
View controllers load their views lazily. So although you have created the view controller, you haven't asked it to load it's view. So the buttons have not been created from the nib. So the properties are nil, and not target action is getting set.
The first option to fixing it is to move the last line so it looks like this:
[self.tableView addParallaxWithView:header.view andHeight:229];
[header.aboutUs addTarget:self action:#selector(aboutUsPage:) forControlEvents:UIControlEventTouchUpInside];
[header.MyLibrary addTarget:self action:#selector(myLibraryPage:) forControlEvents:UIControlEventTouchUpInside];
That way you are calling the view, which will initialise it, and the button parameters should not be nil after this.
The classier way of doing this sort of thing is to define a protocol for responding to clicks on the button, and make the calling view controller a delegate that implements the protocol methods. That way you only have to set the delegate when you create the object, and you don't have to configure the buttons each time.
If you write :
[header.aboutUs setTarget:self];
AND if your class (I mean here self) implements the function aboutUsPage:, it should work.

iOS uiview and uiviewcontroller

Hy
i have two classes uiviewcontroller and uiview. I have one view controller. Inside i have uiview. Inside uiview i have textfield and when i write a text and click done i need to refresh uiviewcontroller.
I tried with this in uiview class:
-(IBAction)textFieldReturn:(id)sender
{
ViewController *vc = [[ViewController alloc] init];
[vc viewDidLoad];
}
i need refresh the same as you click the button and open viewcontroller.
I am guessing you mean that you want to "refresh" the view, not the view controller. To do that simply call [self setNeedsDisplay] from the view, or [self.view setNeedsDisplay] from the view controller. Also make sure that the textfield is a subview of the uiview. Either do that in the nib file or in code by calling [self addSubview: (textfield here)].
Also, if you want to access the view controller from the view you will need to create an IBOutlet, simply allocating a new ViewController object within the view does not mean that the created view controller controls the view. Hopefully that makes sense. I'd recommend going through some ios starter tutorials as well. Just google that there are a lot.

removefromsuperview and addsubview not changing display

I'm pretty new to iOS development, and I'm trying to develop a simple app in which a button changes the subviews. I have a base RootViewController, which displays MiddleView correctly on init. MiddleView has a single button, labeled "First," which is connected (in Interface Builder) to RootViewController's -openFirstView.
Here's how MiddleView is displayed within RootViewController's -viewDidLoad
MiddleViewController *middleTemp = [[MiddleViewController alloc] initWithNibName:#"MiddleView" bundle:nil];
self.middle = middleTemp;
self.middle.rootViewController = self;
[self.view addSubview:middle.view];
[middleTemp release];
So I have the following ViewControllerss: MiddleViewController and FirstController which control MiddleView and FirstView respectively, and a RootViewController which switches between the two.
I've linked this by placing a RootViewController reference in MiddleViewController, and adding
self.middle.rootViewController = self;
to RootViewController's -viewDidLoad.
-(IBAction)openFirstView:(id)sender{
[middle.view removeFromSuperview];
[self.view addSubview:firstController.view];
}
Note: I've tried initializing firstController within -openFirstView, and when it initially didn't run, I moved the initialization to -viewDidLoad and have proven that it is initializing from the nib correctly by displaying FirstView directly in -viewDidLoad
Where firstController is loaded to a reference earlier in code. However, when I run the code and click the button, nothing in the view changes.
I've done some more diagnosing. I've found specifically that -ViewDidLoad in rootViewController is being called twice, once on the original load and once on the first click of the button, and I'm not sure exactly why.
It seems you haven't inialize your firstView in the below method
-(IBAction)openFirstView:(id)sender{
[middle.view removeFromSuperview];
//First initialize the firstController
[self.view addSubview:firstController.view];
}

Resources