For iOS development, if you create and add a sub view at runtime, how would you allow it to communicate with the view controller? Since the sub view isn't instantiated within a nib, you can't use Interface Builder to drag a connection to an IBAction method on the controller. I can't see an obvious way to grab a handle to the controller from the view. Or am I looking at it the wrong way and should instead be communicating with it indirectly via NSNotificationCenter?
Most views don't communicate directly with the view controller. Rather, they are connected to an IBOutlet, or they use target-action to call a method on the view controller.
For the former, just declare an IBOutlet property as you normally would and set it after you create the view. (Note that technically, you don't need the IBOutlet keyword as you're not making a connection in IB, but I don't think it hurts anything.)
self.myViewOutlet = myNewView;
If your UIView is a subclass of UIControl, use addTarget: action: forControlEvents: to have methods of your view controller called when certain events happen on the control.
[myNewControl addTarget:self action:#selector(mySelector:) forControlEvents:UIControlEventTouchDragInside];
What are you trying to communicate? IBActions are for predifined (or newly defined if you want to go through the hassle of adding ur UI element to IB) UI elements that can trigger some selector upon some interaction, for that you can always have the same effect programatically... For example, to create a UIButton at run time you can do..
UIButton *button=[[UIButton alloc] initWithFrame:frame];
[button addTarget:self action:#selector(myAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
Anything that you can do in interface builder you can do programatically as well...you just have to look for the methods that will bind actions to the UI element ...hope this helps.
After reading your comment:
You can consider creating the buttons from the view controller instead of the view, probably the might be the correct way to do it (dont know your circumstance though)...If not then either the view can handle your events and use a protocol to delegate the events to the view controller, or you can use a protocol to register as a target to the UI element, there are many ways to go about solving your problem, what are your circumstances?
Answering it myself... I was creating these subviews from within a parent view so didn't have access to the controller there either, so the self in the code examples from the other answers wasn't possible. But then after thinking about it a bit, I've decided to move the code into the controller itself but instead chose to use a delegate rather than a target-action because it's a custom message, not just a simple touch event. Thanks to those who helped.
Related
I have a custom class that inherits from UIControl, to get touch events. It's basically a collection of other controls that acts as one. I'm converting my app from xibs to a storyboard. It's been mostly painless, except for this.
I drag a UIView into my scene, change the class to my custom control, hook it up to outlets on my ViewController. What I want to do with the control when it's clicked is perform a (push) segue to another ViewController. But the outlets in the designer aren't there for my UIControl to set up the segue.
Is it possible to do this? If so, what do I do to the class to enable that?
I don't think so. Looking at the Connections Inspector pane, UIButton and UIBarButtonItem instances get special treatment—an extra "triggered segues" action you can connect to adjacent view controllers:
While changing your view's class to a descendent of UIControl makes the standard suite of control events available, unfortunately Interface Builder does not let you hook up a segue. The best you can do is create a manually-triggered segue from your view controller to another, then connect your custom control to an IBAction method that performs the segue programmatically:
- (IBAction)performManualSegue:(XYZCustomControl *)sender
{
[self performSegueWithIdentifier:#"manualPushSegue" sender:sender];
}
I'm a newbie iOS developer, and I'm currently facing a problem.
I had to add the same UIButton manually on multiple View Controllers.
Now the code for creating it is repeated in every View Controller with that button.
Now I have to code the selector which is called by the button (An action sheet will be called) but I don't know if repeating the code in every controller is the best thing to do.
Is there a way to write only once the code for the action and call it from every view controller?
Thank you
You can create a UIButton Subclass and then add it to each view. The button behavior and properties will be stored in the class and that way you won't have to repeat it.
in xcode create a new file and choose objective c class and set the class name to MyButton or any other name, and set the subclass to UIButton.
Pay attention that in the subclass the UIButton is the self property. so when you wish to set it's properties you should use the self. for example:
self.tag = 1;
[self setImage:...];
//etc
Then in you view controllers you create MyButton like this:
MyButton *button = [[MyButton alloc]initWithFrame:...];
[self addSubView:button];
I created an app in where i have appdelegate class only. I dont create a UIView or a view controller. I just added textFields, labels and buttons in my window.
I want to hide the keyboard when the user taps out of the text boxes. This is normally achieved by the method touchesBegan. For implementing this method, two things we need to do.
One is add in the header file(.h)
Another one is set the textField's delegate. Thats is, textField.delegate=self;
I did those two things. But still the touchesBegan method did not called.
Why? Should i have a UIView or UIViewController class for it?
Thanks in Advance
UIWindow is a subclass of UIView, so you could just hook into those. But this is a seriously bad design pattern. Consider modifying your application so it uses a single view controller, rather than doing everything in the app delegate. Otherwise you're jury-rigging the app delegate to be something it was never designed to do.
touchesBegan is a UIView method. if you want to do it in your setup you should create a UIView subclass, put all your controls in that one and implement touchesBegan in the UIView subclass.
I have a root view controller which loads a custom UIView subclass I have created and adds it as a subview.
Inside this custom UIView subclass I code/generate a UIButton in the awakeFromNib method.
Is there a simple way to access the File Owner without creating a delegate if the UIButton's action method is inside the root view controller?
E.g
[myButton addTarget:[self.file_owner ?] action:#selector(methodInFileOwner:) ....
Using Interface Builder it's still easy to assign a UIView my custom UIView subclass and just drag a UIButton's selector reference to the file owner. Voila!
How is this done through code though? Do I have to create a delegate and use
[myButton addTarget:[self.delegate] ...
?
File's Owner is an Interface Builder concept. It doesn't exist on the programming side, basically, because it's not needed. In interface Builder, File's Owner is the class that instantiates the nib file. Often, it just refers to the class of the nib file you're currently working with. Since you're working with a view controller, the File's Owner is your view controller subclass, and it allows you to make connections to instance variables and methods of that class.
On the programming side, in this case, the equivalent of File's Owner would just be self. And, you access an instance variable, using properties, as self.instanceVariable.
On to your question. If you want the selector method to be in the view controller, that makes perfect sense. But then, the view controller can create the button, set its target/action, and add it as a subview to the custom view. You could do this in -viewDidLoad, which is called after the nib file is loaded and is the standard place where you would make any programmatic additions to the view controller. So, you could do it as follows:
- (void)viewDidLoad {
self.myButton = [[[UIButton alloc] initWithFrame:CGRectMake(x, y, width, height)] autorelease];
self.myButton.buttonType = ...;
[myButton addTarget:self action:#selector(actionMethod)...];
self.myCustomView = [[[MyCustomViewClass alloc] initWithFrame:...] autorelease];
[self.myCustomView addSubview:self.myButton.view];
[super viewDidLoad];
}
The above code is just an example. You can initialize your objects in different ways. In this case, the button would now be an instance variable of the view controller. But, you could just as easily leave it in the custom view and just refer to it as: self.myCustomView.myButton
I hope this is helpful.
Correction: The above code should be in viewWillAppear rather than viewDidLoad. When viewDidLoad is called, the geometry (i.e. the view's bounds) has not yet been set. So, in order to set the frame of any object, it must be done in viewWillAppear.
The target should be an object of the root view controller class. In your UIView subclass you will need a reference to your root view controller class.
If a nested widget is hidden from the controller, then that essentially means that the custom view should manage all aspects of that nested widget. Here are some options (and probably not a complete list of them either):
You could have the custom subview handle UIControl events and propagate them into the button. Your custom subview would implement the methods of UIControl and essentially hand them down to the button.
You could also use a delegate like you mentioned.
Or you could restructure it so that the widget hierarchy is flattened, but their display is nested.
If you plan on making a custom component that you reuse in multiple places, then the first and second options are probably better since they are more flexible. If this is not the case, the third option is probably best since there is actual interaction between the button and the controller.
The Delegates and DataSources section of the Cocoa Fundamentals Guide gives an example of what the code looks like to create delegates.
I was trying to use IB in a slightly different way that I am use to and I can't get it working extending the normal approach I use, when dealing with IB.
Instead of making a new UIViewController and have the view XIB generated for me and everything linked together by Xcode, I would like to just build a small (320x40px) View XIB and link it to my already existing ViewController.
I start out by making a new file in Xcode, select "view XIB".
I then open IB and add some labels etc. to the view and I set "Files Owner" to be my existing ViewController.
In my existing ViewController I set the IBOutlets for the labels etc. I put in my view.
I go back to IB and hook up the UILabels to my outlets in "Files Owner".
I would now think that I have a reference to the labels inside the XIB, in my viewController.
This is not really the approach I would like, I see no need for my viewController to have a reference to labels inside my view.
How I usually do in code:
My ViewController controls a bunch of UIViews made entirely in code and who instantiate them by:
UIView *customView = [[CustomView alloc] initWithFrame:aFrame];
[customView setTag:MY_CUSTOM_VIEW];
[customView setDelegate:self];
[self.view addSubView:customView];
[customView release];
After this I would access the labels, buttons etc. from my controller by using the [(UILabel*)[[self.view viewWithTag:MY_CUSTOM_VIEW] myLabel] setText#"Hello, World"];
have my UIViewController implement what ever methods the customView protocol required.
How to get that functionality with IB
Should I first build a MyCustomView class extending the UIView class, have it hold all my IBOutlets, set MyCustomClass as files owner and then instantiate that as shown above?
Is it OK to have a view act as viewController for the IB view and how would I relay actions to my "real" viewController?
What I would like to achieve is to deal with instantiating and laying out several UIViews in my UIViewControllers code, but have the freedom of designing some of these UIViews in IB.
All the info I can find is regarding the standard "build a UIViewController with a XIB for the view" or "How to build libraries of IB components".
I hope it makes sense and thanks for any help given:)
You can create whatever view structure you want in Interface Builder and then instantiate it using the UINib class. Once you create an UINib object it loads the contents from the nib and keeps them. Then, whenever you send it the instantiateWithOwner:options: message, it will instantiate the objects contained in the xib and return an array with the top level views. You can then add these views to your view hierarchy and handle them just like any other view you created programmatically.
If you keep the UINib object (as a property for example), you can instantiate the contents again and again, which allows your xib to be used like a template.
update: For a pre-iOS 4 workaround see my recent question and answer.