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];
Related
I'm new to interface builder.
I'd like to design a view in interface builder.
I know I can load the view via 'loadNibNamed'.
Now, suppose there will be two buttons(or two tap gesture recognizers) in the view, and when those buttons are touched, some functions need to fire.
How do I connect(?) the touch to some functions of the viewController that I called the 'loadNibNamed' from?
The view will be a pop-up view (PopUp view) with two buttons(or tap gesture recognizers).
You need to set up outlets from your interface builder objects (buttons) to your class.
To do this, you need to make your class that calls "loadNibNamed" as a delegate class. Then synthesize the button so it is a property.
Once you do that add the selector:
[button addTarget:self action:#selector(buttonPressed)
forControlEvents:UIControlEventTouchUpInside];
The better way would be to write a new class for that particular view. Then if you want to catch the event in the class that called "loadNibNamed" you can set the target to something else:
[button addTarget:(UIMyClass)sender action:#selector(buttonPressed)
forControlEvents:UIControlEventTouchUpInside];
There should be lots of tutorials out there :) - btw have you considered upgrading your xcode and using storyboards?
--EDIT:--
The problem happens because you are mixing coding with interface building. I think you still need to make a viewcontroller class for your new view, but you can change the target of the selector to the calling class.
See more here about selectors: #selector and other class (Objective-C)
There is a similar question here about pushing data to a new view programatically:
Can we pass a parameter to view did load or view will appear of other class from a class
If you're new to IB there are three steps.
Add the method name in your .h (header) file, i.e.
-(IBAction)someMethod:(id) sender;
Save the modified .h file.
Open Interface Builder.
Open the File Owner window under the Tools menu I believe.
Cntrl+Click on the File Owner selection under the File Owner Window.
A black window should appear with a bunch of interface options under it. A small black circle should appear next to someMethod
Drag that small black circle (a blue line should appear) onto the button you want your method to connect to. Another menu should appear. Choose an appropriate action (something like on touch or something like that). Repeat for the second button.
In the IB for each button give them a tag in the attributes list under the Properties (I believe). If you need the Properties window look under the Tools menu again. Go to the Tag section and give each button a different tag. (1, 2, 3 ... etc).
Implement the method in the .m file. Make sure you differentiate your actions for which button the user selects, i.e. if (sender.tag == 1) {...} else if (sender.tag == 2) {...}
Save and run.
The end.
I'm trying to create a second view controller and link it to a new set of .m & .h files then alter the text of a label on that view controller under the viewDidLoad but am having some issues.
Here's what I am doing so far.
I'm starting with a blank new single page, from the MainStoryboard, I add a new VC and add buttons so I can navigate between the pages. This works fine.
Next I create a new Objective-C Class, "SecondViewController" and choose subclass UIViewController.
Now back on the Storyboard I select the other ViewController and try to pick the new SecondViewController as a Custom Class but it doesn't show up in the list of available classes.
Now, if in step 2 I choose the UIView subclass I can assign it to the ViewController in step 3 but the viewDidLoad options are not available so I am not sure how to program changes to the VC.
What am I missing here?
I am able to use the UIView and use the following to make the change I am asking about. I am not sure if this is the correct way to do this.
- (void)drawRect:(CGRect)rect
{
myLabel.text = #"change2";
}
EDIT ----
Okay, I've figured it out. I was clicking on the VC's View (background) in the Storyboard and not the actual ViewController! Now it works the way I thought it should. I suppose if I had looked at the View Controller Scene I would have noticed what I was doing incorrectly.
Now, if in step 2 I choose the UIView subclass I can assign it to the
ViewController in step 3 but the viewDidLoad options are not available
so I am not sure how to program changes to the VC.
The class of your ViewController (or similar one) from interface builder and another class in your code must extend the same class
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.
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.
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.