How to go about customizing UIButton - objective-c

I'm trying to customize my buttons by using a category but I don't want all my buttons in my project to use this category. Is there a way to let the button know it should/shouldn't use the category (Like some sort of bool isUsingCategory). If not, whats the best way to go about this? I would subclass it but I hear that subclasssing UIButton is a bad idea. Is there a way to exclude categories from certain objects? Thanks!
EDIT: What I have right now that's working is in my controller running a special method to draw the buttons but I don't think thats follows MVC very well.

You just need to inherit from the UIButton (for example MyButton) and add one property something like this:
#property (nonatomic, assign) BOOL useCategoryBehavior;
In the category of MyButton use this property before do something in overriden methods. Like this:
- (void)viewDidLoad {
[super viewDidLoad];
if (useCategoryBehavior) {
// do smth here
}
}

Related

Two States -> Two Actions -> One UIButton for iOS 7

I want to accomplish something that It's even hard for me to explain to you, but I'm going yo try to do.
I have one UIButton (Button 1) and want it do to two different actions depending on the context or state of this particular button, but the context or state is controlled by another UIButton (Button 2).
I hope this image can explain what I'm looking for:
Now... i'm not looking for the exact code I need to implement, but an idea of how to accomplish this, maybe some hint about some methods or classes to use.
Note: I already think about to change the UIButton tag to control the action, but I can't change the tag because I need it as it is to control the behavior of actions. Also, (Button1) must return to it's original state once the second action it's used.
From what you've described, I suggest you should have two IBActions and set a property of UIButton 1. Your .h header file will look something like -
-(IBAction)button1Pressed:(id)sender;
-(IBAction)button2Pressed:(id)sender;
#property (strong, nonatomic) IBOutlet UIButton* button1;
Where the button1 property is hooked up to your button one via ctrl-drag in the interface builder.
Then within your .m implementation file, the following set-up will enable you to do what you're looking for.
-(IBAction)button2Pressed:(id)sender{
if (self.button1.selected){
self.button1.selected = NO;
}else
self.button1.selected = YES;
}
So when you press button2, this will change the state of button1 by making it 'selected' or not, which means button1 will do what ever you need, where the state is controlled by button2.
-(IBAction)button1Pressed:(id)sender{
if (self.button1.selected){
//Place your code here for button 1 to do something in this state
} else
//Place your code here for button 1 to do something in this UN-selected state
}
I hope this helps with what you're trying to do.
Thanks, Jim.
You can use the UIButton's UIControlState, with if statements.
(i.e
buttton.selected....
buttton.enabled.....
buttton.highlighted....
)
if I understand correctly?

How to access objects from different classes in Cocoa Programming

I have an NSTextField subclass (called "txtField1" and used as Custom Class for a Text Field in my interface builder) and I would like to be able to access an NSComboBox object which present in my interface builder from this class.
This is my code:
txtField1.h:
#import <Cocoa/Cocoa.h>
#interface txtField1 : NSTextField
#end
txtField.m:
#import "txtField1.h"
#implementation txtField1
-(void)mouseDown:(NSEvent *)theEvent
{
HERE I would like to be able to write something like:
[combobox SetHidden:YES];
}
#end
I would like to be able to set access the combobox SetHidden property, in the mouseDown event.
Can you please tell me how to do that? I have tried different solutions found on internet but didn't obtain anything at all!
Any help would be appreciated.
Here are a lot of ways, and answers here, to do :
Update a label through button from different view
Xcode - update ViewController label text from different view
Setting label text in another class
Set label on another view to stored NSDate
EDIT:
-(void)mouseDown:(NSEvent *)theEvent
{
HERE I would like to be able to write something like:
[combobox SetHidden:YES];
/*
use the shared instance of comboBox here and make it hidden.
Also, you can use binding to make it hidden
*/
}
From my point of view txtField1 class is not better place to this code.
You can add NSControlTextEditingDelegate protocol to your NSViewController implementation (that already contains IBOutlets for txtField1 and combobox) and in method – control:textView:doCommandBySelector: implement hiding of your NSComboBox

Accept Both UIButtons and UIViews as parameters for method

How can I write a function that will take either a UIView or a UIButton as a parameter.
For example, let's say I wanted to round the corners the View/Button:
[self roundCorners:x]
How could I make that accept both a UIButton object or a UIView object as x?
My first thoughts were use id or NSObject, but I don't think that is correct:
-(void)roundCorners:(id)objectToRound;
UIButton is a subclass of UIView, so your method will take either one if you declare it this way:
- (void)roundCorners:(UIView *)objectToRound;
Short answer:
if ([view isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *) view;
// do something funky with button
} else {
// do something funky with view
}
Detailed answer:
First of all, it is usually best to have a specific method to handle each class of object. This is so you can have the compiler resolve potentially passing the wrong class to a method. There are however times when this is not appropriate or possible.
UIView and UIButton are both ultimately a descendant of NSObject.
NSObject has a method isKindOfClass: which you can use to determine what you are dealing with. I have taken it a step further by defining a few helper macros that simplify these sorts of tasks, which are sometimes quite laborious.
If you have a look at my answer to this question... you will see source code for, and an example of using these macros. I often use one of these macros (asClass), which combines a isKindOfClass: test linked to a type cast to the class being tested for.
You can code such a thing long hand, however I find it creates more readable code to use the asClass macro. Put simply asClass(myView,UIButton) will either resolve to nil or to a type cast reference to myView as a UIButton - if it is not a UIButton, it will be resolved to nil.
I have modified a snippet from that post to suit your question:
- (void)myMethod:(UIView*)view{
UIButton *button = asClass(view, UIButton);
if (button) {
// do something funky with button
} else {
// do something funky with view
}
}
To code this long hand, (without macros) it would look approximately like this:
- (void)myMethod:(UIView*)view{
if ([view isKindOfClass:[UIButton class]]) {
UIButton *button = (UIButton *) view;
// do something funky with button
} else {
// do something funky with view
}
}
As you can see, it's not too much extra code to do it long hand, and if you are not comfortable using macros for things like this (some people aren't) then you may choose to stick with the second example.
If you are comfortable with macros, and can see the merits of the approach I have taken, consider this third way of doing it:
- (void)myMethod:(UIView*)view{
UIButton *button;
if ( (button = asClass(view, UIButton)) ) {
// do something funky with button
} else {
// do something funky with view
}
}
The way you do it should work just fine, id will accept almost anything.

Populate text in one textfield from text in another

Working on an experiment on the iPad. Tried some variations on how to do this, but I can't seem to get it to work correctly...
I tap a UIButton on my MainViewController and a TextEntryModule is added to the view. TextEntryModule is its own class (for multiple instantiation) and it contains a UITextView called TextEntry (this all works at the moment).
I tap on the TextEntry UITextView and it brings up the keyboard and another view (located in MainViewController) with a UITextView called TextPreview. (this also works at the moment).
The part I'm having trouble with is synching the two UITextViews. The idea being that when I type into TextEntry, the text in TextPreview will also be updated.
Outlets are linked properly for the text fields, but I think I'm missing something "obvious":
TextEntryModule *tm = (AnnotationModule *)currentModule;
TextPreview.text = tm.TextEntry.text
Thanks in advance!
UITextView: delegate.
- (void)textViewDidChange:(UITextView *)textView
Then assign it the value of the other textview in this method.
Edit
#interface MainViewController <UITextViewDelegate> {
...
}
...
#end
Then you implement this method in the implementation file of MainViewController
#implementation MainViewController
//More code
- (void)textViewDidChange:(UITextView *)textView {
TextEntryModule *tm = (AnnotationModule *)currentModule;
TextPreview.text = tm.TextEntry.text
}
#end
Then you will have to set the TextEntryModule object's delegate to self since the controller now conform to the protocol and can "act" upon this notification.
You need to become a UITextFieldDelegate and monitor when text changes in the one field and then update the other field. Take a look at the documentation on it.

refreshing mkannotation properties and refreshing the annotation

I have a mkannotation located on a mapview which has a mkannotationview as well as a calloutview which when clicked goes to a child uiviewcontroller. I am updating some properties from the callout's uiviewcontroller, but after I'm finished I want to move the annotation's position on the map and change the annotation title and subtitle. How can I easily do this from the callout's uiviewcontoller? What's the most elegant way to handle this? A code sample would be great if anyone has any.
Thanks
I'd create a protocol, say MapCallBackDelegate, to handle what you want to do. This avoids tightly coupled code. Put this in your map annotation view header file
#protocol MapCallBackDelegate
-(void)updateAnnotation:(id)whatEverParamsYouWant;
#end
Then make your Map View implement this protocol. When you create your map annotation view, give it a property
#property (nonatomic, retain) id<MapCallBackDelegate> callbackDelegate;
And when you add it to your map, set that property to self
myMapAnnotationView.callbackDelegate = self;
so when you want to change the title/subtitle/position, you just invoke that message on the callbkacDelegate.
This is elegant because it reduces tightly-coupled code, allows other objects to implement the same protocol for code reuse later, and promotes information hiding in your MapAnnotationView.
Remove the annotation from the map entirely, update it, and add it to the map again. That'll ensure that the map notices that the annotations location has changed.
Although you can remove and add the annotation back as #Caleb suggests, another option is to update the coordinate property directly on the annotation you want to move.
Note that this will only work if your annotation class implements setCoordinate which can easily be done by declaring the coordinate as assign (like the built-in MKPointAnnotation class does) instead of readonly. The map view will see the change via KVO and move the annotation.
To have the child view controller tell the map view controller which annotation to change and what the new coordinates are, I recommend using delegate+protocol as another answer suggests.
The easiest way would to actually not do it from the child view controller. Maybe your needs are different from what I understand from the question, but at first blush I would do something like this:
In the header:
#interface YourController
{
...
MKAnnotation *_latestDetailViewed;
}
...
#property(nonatomic, retain) MKAnnotation *latestDetailViewed;
#end
Then in the .m something like
#implementation YourController
...
#synthesize latestDetailViewed = _latestDetailViewed;
...
-(void) dealloc
{
...
self.latestDetailViewed = nil;
[super dealloc];
}
-(void) whereverYouLaunchYourDetailScreenFrom:(MKAnnotation*)detailAnnotation
{
self.latestDetailViewed = detailAnnotation;
// then create/push your view controller
}
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if(_latestDetailViewed)
{
// Do whatever you want to the annotation here
self.latestDetailViewed = nil;
}
}
That way your changes will be made when you come back to the map. If you're really only launching a detail view for one annotation at a time, and always coming back to the map in between, then it should work without making you deal with writing a delegate protocol or firing off NSNotifications.
If I'm misunderstanding your situation let me know and I'll give you a different answer :)