I've just started learning the basics of Xcode and Objective C and I am making a basic torch app as a starting point.
I've already picked up most of the basics of things, but I don't know how to make a reference to another element from an separate action (I have no idea if this terminology is correct).
For example:
- (IBAction)screenButtonClicked:(id)sender {
UIButton *button = (UIButton *)sender;
if (_ScreenOnOff) {
[self.view setBackgroundColor:[UIColor blackColor]];
[button setTitle:#"Screen (Off)" forState: UIControlStateNormal];
}
else {
[self.view setBackgroundColor:[UIColor whiteColor]];
[button setTitle:#"Screen (On)" forState: UIControlStateNormal];
}
_ScreenOnOff = !_ScreenOnOff;
}
I have a button on the storyboard which is linked to that, and I have a UIImageView which I want to show and hide (depending on the if's).
I've looked everywhere about how to do this and put it as many ways as I can into Google, but no luck.
This might be a baby step in Objective C, but please help as it will teach me.
Thanks in advance.
Declare an IBOutlet for the UIImageView in the header file:
#property (weak) IBOutlet UIImageView *myImageView;
After that, connect the UIImageView to this IBOutlet in Interface Builder.
Then, you can reference (and hide it) like:
self.myImageView.hidden = _ScreenOnOff;
in your implementation file.
See also: Creating and Connecting an Outlet
This action is probably owned by your view controller. If your view controller has a UIImageView property named 'imageView' you can access it from inside your action just like you're doing with the _ScreenOnOff.
To hide your image view you could do something like this:
self.imageView.hidden = YES;
Note that it is important to use self.imageView and self.ScreenOnOff instead of accessing directly the property by _imageView. When you declare a property in your class (be it an IBOutlet or not) the compiler synthesizes accessor methods to that property (get and sets). So when you call self.ScreenOnOff it would be the similar of doing [self ScreenOnOff].
The only places where you will access the property directly by '_' is inside init and dealloc methods. '- (void) viewDidLoad' is on type of init method. I guess you want something like this:
- (IBAction)screenButtonClicked:(id)sender {
UIButton *button = (UIButton *)sender;
if (self.ScreenOnOff) {
[self.view setBackgroundColor:[UIColor blackColor]];
[button setTitle:#"Screen (Off)" forState: UIControlStateNormal];
self.imageView.hidden = YES;
}
else {
[self.view setBackgroundColor:[UIColor whiteColor]];
[button setTitle:#"Screen (On)" forState: UIControlStateNormal];
self.imageView.hidden = NO;
}
self.ScreenOnOff = !self.ScreenOnOff;
}
I hope that helps.
Related
Ok, so till now i have been declaring all my view objects in .h file. For e.g:
UIButton *btnCustomFacebookLogin;
And define in my .m file like:
btnCustomFacebookLogin = [UIButton buttonWithType:UIButtonTypeCustom];
btnCustomFacebookLogin.backgroundColor = [UIColor darkGrayColor];
btnCustomFacebookLogin.frame = CGRectMake(0,0,180,40);
btnCustomFacebookLogin.center = CGPointMake(self.view.center.x, 200);
[btnCustomFacebookLogin setTitle: #"Facebook Login" forState: UIControlStateNormal];
[btnCustomFacebookLogin addTarget:self action:#selector(facebookLoginButtonClicked) forControlEvents: UIControlEventTouchUpInside];
[self.view addSubview:btnCustomFacebookLogin];
And i have a few more buttons in my app which are all defined in my
- (void)viewDidLoad
I got this checked from my mentor and he told me that all these buttons should be in a separate method and not in viewDidLoad, i have no idea where they should be, i went through some sample codes on the internet and could not find a clue. What is the proper place where all the buttons are defined ? Since i work for a company now i have to follow what conventions are told to me.
Keep your IBOutlets properties outside of header file (*.h) and declare them inside implementation file (*.m) in interface extension of your View Controller.
In your implementation file (*.m) :
#interface MyViewController ()
#property (nonatomic, weak) IBOutlet UIButton *myButton;
#end
Remember to set your property behaviour nonatomic as by default each property gets atomic behaviour and when using UI objects there is no need to make them thread safe. Set it to weakas you are not a direct owner of the object, its lifecycle belongs to your storyboard (assuming you use storyboard).
Meet Doshi is correct about using viewDidLoad and performing selector from inside it, to make an initial setup for your UI elements. It makes your code clear and readable.
I would possibly call it:
- (void)styleUI {
//insert your code here
}
If you find yourself to set specific UIButton styling in different View Controllers in this same way, then to avoid boilerplate code you could create own Class Category for UIButton and specify this same style in one place.
Create a new file selecting Objective-C File.
In File type prefix for your project with name for class (example:
MAPStyling).
Select Category for File Type.
Select Class that you want to create a category for (for UIButton select UIButton).
Inside you category class in implementation file (*.m) add method for your styling:
- (void)map_applyFacebookStyle
// insert your code here but instead to name of your button refer to self.
self.title = #"Facebook Login";
}
Expose method in header file of your category by adding method signature into #interface section.
#interface UIButton (MAPStylig)
- (void)map_applyFacebookStyle;
#end
In your View Controller implementation file (*.m) import your Class Category and modify your method:
- (void)styleUI {
[btnCustomFacebookLogin map_applyFacebookStyle];
}
This will call #selector of your styling method from your Class Category on your UIButton. As you can imagine in this way you can remove a lot of boilerplate code from every View Controller in your application.
Your mentor says, "All these buttons should be in a separate method and not in viewDidLoad". Means you are setting frames and properties to all buttons into viewDidLoad method. He didn't said that you can't define all objects into .h file. So he says, you have do that into other method. Just use following code.
I think, right now you did this:-
- (void)viewDidLoad
{
btnCustomFacebookLogin = [UIButton buttonWithType:UIButtonTypeCustom];
btnCustomFacebookLogin.backgroundColor = [UIColor darkGrayColor];
btnCustomFacebookLogin.frame = CGRectMake(0,0,180,40);
btnCustomFacebookLogin.center = CGPointMake(self.view.center.x, 200);
[btnCustomFacebookLogin setTitle: #"Facebook Login" forState: UIControlStateNormal];
[btnCustomFacebookLogin addTarget:self action:#selector(facebookLoginButtonClicked) forControlEvents: UIControlEventTouchUpInside];
[self.view addSubview:btnCustomFacebookLogin];
}
So, just put this code into one method and call this method from viewDidLoad like this:-
- (void)viewDidLoad
{
[self settingPropertiesOfButtons];
}
- (void)settingPropertiesOfButtons
{
//insert your code here..
btnCustomFacebookLogin = [UIButton buttonWithType:UIButtonTypeCustom];
btnCustomFacebookLogin.backgroundColor = [UIColor darkGrayColor];
btnCustomFacebookLogin.frame = CGRectMake(0,0,180,40);
btnCustomFacebookLogin.center = CGPointMake(self.view.center.x, 200);
[btnCustomFacebookLogin setTitle: #"Facebook Login" forState: UIControlStateNormal];
[btnCustomFacebookLogin addTarget:self action:#selector(facebookLoginButtonClicked) forControlEvents: UIControlEventTouchUpInside];
[self.view addSubview:btnCustomFacebookLogin];
}
That's it. If you could framing those buttons in .storyboard file or in .xib, then its better.
Let's say that I wish to approach a certain UIControl in a certain method that it triggers - I can send the method a pointer to the UIControl using "sender". but then, for some reason, I cannot approach sender's properties directly and have to use the setX:forState: methods. If I approach the properties directly, I get no error or warning; it simply does nothing...
Here's an example:
h. file...
#interface MYViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIButton *dButton; //connected to a UIButton in IB
-(IBAction)dButtonClick:(UIButton*)sender; //connected to the same UIButton in IB
#end
then...
m. file...
- (void)viewDidLoad
{
[super viewDidLoad];
self.dButton.titleLabel.textColor = [UIColor greenColor]; //this is working...
}
-(IBAction)dButtonClick:(UIButton*)sender
{
sender.titleLabel.textColor = [UIColor redColor]; //this is not working...
self.dButton.titleLabel.textColor = [UIColor redColor]; //this is also not working...
[sender setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; //only this is working.
//But why?!?!?
}
I tried to search for some info about the way these items work, but couldn't find anything that explains the logic behind it clear enough.
Any help would be... well... helpful...
This:
sender.titleLabel.textColor
And this:
[sender setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
Are different not just because of the dot notation but also because of the state. The button has different colours for each state so any colour set directly will be overridden by the colour for the state.
This line:
self.dButton.titleLabel.textColor = [UIColor redColor];
Could be the same issue or you may just not have connected the outlet.
I am mocking up a quick demo of a project but am having a problem with a UITextField.
The behavior that we want is that when a user clicks on a button, there should be a custom view that appears with a UITextField and a UIButton in a custom view that overlays the main view.
I have a custom view called Searchview and the following in the Searchview.m. The problem is that when the textField is a property, it doesn't show but when it is a local variable, it does show. Can anybody help me with what is going on so that the UITextField shows? Is how I am doing this even the right idea (custom UIView or custom UIControl or a modal controller)? Finally, would setNeedsDisplay be appropriate here?
thx in advance
#interface Searchview()
#property (nonatomic, weak) UITextField *textField;
#end
- (void)drawRect:(CGRect)rect
{
// this doesn't work
self.textField = [[UITextField alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 120.0f, 25.0f)];
self.textField.returnKeyType = UIReturnKeyDone;
self.textField.placeholder = #"Writer";
self.textField.borderStyle=UITextBorderStyleBezel;
[self.textField addTarget:self
action:#selector(textFieldDone:)
forControlEvents:UIControlEventEditingDidEndOnExit];
[self addSubview: self.textField];
/* this works
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(10.0f, 10.0f, 120, 25)];
textField.returnKeyType = UIReturnKeyDone;
textField.placeholder = #"Writer";
textField.borderStyle=UITextBorderStyleBezel;
[textField addTarget:self
action:#selector(textFieldDone:)
forControlEvents:UIControlEventEditingDidEndOnExit];
[self addSubview: textField];
*/
UIButton *mButton=[UIButton buttonWithType:UIButtonTypeRoundedRect];
mButton.frame=CGRectMake(200.0f,10.0f,100.0f,37.0f);
[mButton setTitle:#"search" forState:UIControlStateNormal];
[mButton addTarget:self action:#selector(showSearchController:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:mButton];
[self setNeedsDisplay];
}
As a property - not showing:
As a local variable - showing:
#property (nonatomic, strong) UITextField *textField;
change the weak to strong and change the self.textFiled to _textField to have a try
And make sure your textField property not be released
It's pretty simple when you think about it. ARC (approximately) converts the following code:
self.weakProp = [[Foo alloc] init];
to the equivalent of the following "manually reference-counted" code:
Foo * temp = [[Foo alloc] init];
self.weakProp = temp;
[temp release];
Nothing is retaining it, so it is released.
I can only think of two reasons to have assign/weak IBOutlets:
For an outlet in a VC, so it doesn't retain a subview when its view is set to nil (e.g. on a memory warning). This is less relevant in iOS 6.0 since views are not automatically released on a memory warning (so if you do it, you can release them all explicitly).
For a view where the outlet points to a superview (and would cause a retain cycle). This is quite rare.
In general, I prefer strong IBOutlets: They might keep objects alive for a little longer than necessary, but they are safer than assign and more efficient than weak. Just watch out for retain cycles!
How do I set a tag for a button programmatically?
I later want to compare to tags for a conclusion
I've tried this
-(IBAction)buttonPressed:(id)sender{
NSLog(#"%d", [sender tag]);
}
but that just crashes the app.
Any other ideas?
You need to cast sender as a UIButton:
-(IBAction)buttonPressed:(id)sender{
UIButton *button = (UIButton *)sender;
NSLog(#"%d", [button tag]);
}
Edit: Regarding the message "unrecognized selector"...
Based on your error message, it's not able to call the buttonPressed method in the first place. Notice in the error message it is looking for "buttonPressed" (no colon at end) but the method is named "buttonPressed:". If you are setting the button target in code, make sure the selector is set to buttonPressed: instead of just buttonPressed. If you are setting the target in IB, the xib may be out of sync with the code.
Also, your original code "[sender tag]" should also work but to access button-specific properties, you'll still need to cast it to UIButton.
I know this is an old question and been answered many a time in other questions, but it came up in a google search as second from the top. So, here is the answer to why it was crashing. Change it to 'button.tag'
-(void)myMethod
{
UIButton *theButton = [UIButton buttonWithType:UIButtonTypeCustom];
[theButton addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchDown];
theButton.tag = i;//or whatever value you want. In my case it was in a forloop
}
-(void)buttonPressed:(id)sender
{
UIButton *button = (UIButton *)sender;
NSLog(#"%d", button.tag);
}
No need for casting. This should work:
-(IBAction)buttonPressed:(UIButton*)sender
{
NSLog(#"%d", [sender tag]);
}
When using drawRect for a custom UIButton subclass, it never seems to get called to draw the button when highlighted. Do I need to call setNeedsDisplay for my button in my touch events?
I found an easy solution.
Just add the following method to your UIButton subclass:
-(void)setHighlighted:(BOOL)highlighted
{
[super setHighlighted:highlighted];
[self setNeedsDisplay];
}
That's it!
As far as i can tell there is no straight forward way to subclass UIButton.
UIButton is not the actual class type that is returned by the initializers. UIButton is kind of a front for a series of private classes.
Say you had:
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
NSLog(#"myButton type: %#", [myButton description]);
You will find the type returned in the log to be "UIRoundedRectButton". The problem with that is you would need to have extended "UIRoundedRectButton". That is not possible as it is a private class which is only ever returned to UIButton.
On top of that "UIRoundedRectButton" is not the only possible returned class all of which are private.
In other words UIButton was built in manner that is not suited to be extended.
I had the same problem and satisfying success with the following added to my UIButton subclass
- (void)awakeFromNib {
[self addTarget:self action:#selector(redraw) forControlEvents:UIControlEventAllEvents];
}
- (void)redraw {
[self setNeedsDisplay];
[self performSelector:#selector(setNeedsDisplay) withObject:self afterDelay:0.15];
}