Subclass UIButton to add a property - iphone

I'd like to subclass UIButton to add some properties that i need (not methods... only properties).
Here the code of my subclass:
//.h-----------------------
#interface MyButton : UIButton{
MyPropertyType *property;
}
#property (nonatomic,retain) MyPropertyType *property;
#end
//.m--------------------------
#implementation MyButton
#synthesize property;
#end
And here how I use the class:
MyButton *btn = ((MytButton *)[MyButton buttonWithType:UIButtonTypeRoundedRect]);
btn.property = SomeDataForTheProperty;
From where i obtain this error :
-[UIRoundedRectButton setProperty:]: unrecognized selector sent to instance 0x593e920
Thus, from ButtonWithType i obtain a UIRoundedRectButton and (Mybutton *) can't cast it...
What i have to do to obtain a MyButton object ? is -init the unique solution ?
Thank you!

Try using a category with Associative References instead. It is much cleaner and will work on all instances of UIButton.
UIButton+Property.h
#import <Foundation/Foundation.h>
#interface UIButton(Property)
#property (nonatomic, retain) NSObject *property;
#end
UIButton+Property.m
#import "UIButton+Property.h"
#import <objc/runtime.h>
#implementation UIButton(Property)
static char UIB_PROPERTY_KEY;
#dynamic property;
-(void)setProperty:(NSObject *)property
{
objc_setAssociatedObject(self, &UIB_PROPERTY_KEY, property, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSObject*)property
{
return (NSObject*)objc_getAssociatedObject(self, &UIB_PROPERTY_KEY);
}
#end
//Example usage
#import "UIButton+Property.h"
...
UIButton *button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button1.property = #"HELLO";
NSLog(#"Property %#", button1.property);
button1.property = nil;
NSLog(#"Property %#", button1.property);

You need to do:
MyButton *btn = [[MyButton alloc] init];
To create your button. The buttonWithType:UIButtonTypeRoundedRect only creates UIButton objects.
=== edit ===
If you wish to use a RoundedRect button; then I would suggest the following. Basically, we just create a UIView with whatever properties we want and add the desired button to that view.
.h
#interface MyButton : UIView
{
int property;
}
#property int property;
#end
.m
#implementation MyButton
#synthesize property;
- (id)initWithFrame:(CGRect)_frame
{
self = [super initWithFrame:_frame];
if (self)
{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btn.frame = self.bounds;
[self addSubview:btn];
}
return self;
}
#end
Usage:
MyButton *btn = [[MyButton alloc] initWithFrame:CGRectMake(0, 0, 200, 20)];
btn.property = 42;
[self.view addSubview:btn];

I have a simple scheme that only involves a few library methods, no boilerplate, and just 3 lines of code for each property you want to add. There are two example properties added below: startPoint and tileState. For illustrative purposes here are the lines you'd need to add for a property like tileState:
//#property (assign, nonatomic) SCZTileState tileState; // tileState line 1
//#property (assign, nonatomic) SCZTileState tileState; // tileState line 2
//#dynamic tileState; // tileState line 3
There's more details in my blog post describing how this works
UIButton+SCZButton.h
#import <UIKit/UIKit.h>
#interface UIButton (SCZButton)
#property (readwrite, nonatomic) id assocData;
#end
UIButton+SCZButton.m
// UIButton+SCZButton.m
// Copyright (c) 2013 Ooghamist LLC. All rights reserved.
#import "UIButton+SCZButton.h"
#import <objc/runtime.h>
#implementation UIButton (SCZButton)
- (id)assocData {
id data = objc_getAssociatedObject(self, "SCZButtonData");
return data;
}
- (void)setAssocData:(id)data {
objc_setAssociatedObject(self, "SCZButtonData", data,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
OOGTotallyTile.h
// UIButton+OOGTotallyTile.m
// Copyright (c) 2013 Ooghamist LLC. All rights reserved.
#import <UIKit/UIKit.h>
#import "UIButton+SCZButton.h"
#define kPointLabelTag 837459
typedef enum {
SCZTileStatePlaced,
SCZTileStateDropping,
SCZTileStateDropped
} SCZTileState;
#interface SCZButtonData : NSObject
#property (assign, nonatomic) CGPoint startPoint;
#property (assign, nonatomic) SCZTileState tileState; // tileState line 1
#end
#interface UIButton (OOGTotallyTile)
#property (readonly, nonatomic) SCZButtonData *buttonData;
#property (assign, nonatomic) CGPoint startPoint;
#property (assign, nonatomic) SCZTileState tileState; // tileState line 2
#end
OOGTotallyTile.m
// UIButton+OOGTotallyTile.m
// Copyright (c) 2013 Ooghamist LLC. All rights reserved.
#import "OOGTotallyTile.h"
#implementation SCZButtonData
#end
#implementation UIButton (OOGTotallyTile)
#dynamic startPoint;
#dynamic tileState; // tileState line 3
- (SCZButtonData*)buttonData {
if ( ! self.assocData) {
self.assocData = [[SCZButtonData alloc] init];
}
return self.assocData;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
id forwardingTarget = [super forwardingTargetForSelector:aSelector];
if ( ! forwardingTarget) {
return [self buttonData];
}
return forwardingTarget;
}
#end

Related

Changing UIButton(in this case a playing card)'s title randomly

New to ios programming so for practice I am trying to make a simple app with a playing card with it's back on the view which you can tap to reveal the front of the card. The front of the card is different every time you tap it.
What I am trying to achieve is that when i tap the card:
it flips and displays a random card,
have a label that increments by 1 every time the card is flipped
I am done with the second requirement but I can't seem to get the first one right. I have 3 models(Card, Deck, PlayingCard)
My Deck model is responsible for randomising the cards and here are the codes.
Deck.h
#import <Foundation/Foundation.h>
#import "Card.h"
#interface Deck : NSObject
- (void)addCard:(Card *)card atTop:(BOOL)atTop;
- (Card *)drawRandomCard;
#end
Deck.m
#import "Deck.h"
#interface Deck()
#property (strong, nonatomic) NSMutableArray *cards;
#end
#implementation Deck
-(NSMutableArray *)cards
{
if(!_cards) _cards =[[NSMutableArray alloc] init];
return _cards;
}
- (void)addCard:(Card *)card atTop:(BOOL)atTop
{
if(card){
if(atTop){
[self.cards insertObject:card atIndex:0];
}
else
{
[self.cards addObject:card];
}
}
}
- (Card *)drawRandomCard
{
Card *randomCard = nil;
if(self.cards.count){
unsigned index = arc4random() % self.cards.count;
randomCard = self.cards[index];
[self.cards removeObjectAtIndex:index];
}
return randomCard;
}
#end
My PlayingCard basically describes the contents of a Card. for eg. Jack of Hearts, 2 of Diamonds where Jack would be the "rank" of the card and "hearts" would be the suit of the card which together form the contents of the card.
PlayingCard.h
#import <Foundation/Foundation.h>
#import "Card.h"
#interface PlayingCard : NSObject
#property (strong, nonatomic) NSString *suit;
#property (nonatomic) NSUInteger rank;
+ (NSArray *) validSuits;
+ (NSUInteger) maxRank;
- (NSString *)contents;
#end
PlayingCard.m
#import "PlayingCard.h"
#implementation PlayingCard
- (NSString *)contents
{
NSArray *rankStrings = [PlayingCard rankStrings];
return [rankStrings[self.rank] stringByAppendingString:self.suit];
}
#synthesize suit = _suit;
+(NSArray *)validSuits{
return #[#"♥", #"♦", #"♠", #"♣"] ;
}
+(NSArray *)rankStrings{
return #[# "?", #"A", #"2", #"3", #"4", #"5", #"6", #"7", #"8", #"9",#"10", #"J", #"Q", #"K"];
}
-(void)setSuit:(NSString *)suit
{
if([[PlayingCard validSuits] containsObject:suit]){
_suit = suit;
}
}
-(NSString *)suit{
return _suit ? _suit: #"?";
}
+ (NSUInteger)maxRank {
return [self rankStrings].count-1;
}
-(void)setRank:(NSUInteger)rank{
if(rank <= [PlayingCard maxRank]) {
_rank = rank;
}
}
#end
Finally my ViewController.m
#import "CardGameViewController.h"
#import "Card.h"
#import "Deck.h"
#import "PlayingCard.h"
#interface CardGameViewController ()
#property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
#property (nonatomic) int flipCount;
#property (nonatomic) NSString *title;
#property (weak, nonatomic) IBOutlet UIButton *cardRandom;
#end
#implementation CardGameViewController
- (void)setFlipCount:(int)flipCount
{
_flipCount = flipCount;
self.flipsLabel.text = [NSString stringWithFormat:#" Flips:%d", self.flipCount ];
}
- (IBAction)flipCard:(UIButton *)sender
{
if(sender.isSelected) {
sender.selected = NO;
self.flipCount ++;
}
else{
sender.selected = YES;
self.flipCount ++;
}
}
#end
I researched about this and i found that I should probably use
- (void)setTitle:(NSString *)title forState:(UIControlState)state
where the state will be UIControlStateSelected because the front of the card containing the contents is the selected content of the button but I dont know how to set the title according to the random card content developed by my model.
Lets assume you have a property of the class Deck (called cardDeck) in your CardGameViewController, and you have already populated that property with cards using addCard method.
Now when the user taps flip button, you should do something like this ::
- (IBAction)flipCard:(UIButton *)sender
{
//draw a random card
Card *randomCard = [self.cardDeck drawRandomCard];
//set the button title
[sender setTitle:[randomCard contents] forState:UIControlStateSelected];
if(sender.isSelected) {
sender.selected = NO;
self.flipCount ++;
}
else{
sender.selected = YES;
self.flipCount ++;
}
}
Hope this helps
PS :: drawRandomCard method has a return type "Card", it seems it should be "PlayingCard", or the PlayingCard class should be named Card.
You aren't initializing your playing card deck. Your ViewController.m file should look like this (I also reset the deck once all cards have been shown in the flipCard method):
#import "CardViewViewController.h"
#import "PlayingCardDeck.h"
#interface CardViewViewController ()
#property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
#property (nonatomic) int flipCount;
#property (nonatomic) PlayingCardDeck *myDeck;
#end
#implementation CardViewViewController
-(PlayingCardDeck *)myDeck
{
if(!_myDeck) _myDeck=[[PlayingCardDeck alloc] init];
return _myDeck;
}
-(void)setFlipCount:(int)flipCount
{
_flipCount= (int) flipCount;
self.flipsLabel.text=[NSString stringWithFormat:#"Flips: %d",self.flipCount];
}
- (IBAction)flipCard:(UIButton *)sender
{
if (!sender.isSelected)
{
if (self.flipCount >= 52)
{
self.flipCount = 0;
self.myDeck = nil;
}
Card *cCard = [self.myDeck drawRandomCard];
[sender setTitle: [cCard contents] forState:UIControlStateSelected];
self.flipCount++;
}
sender.selected = !sender.isSelected;
}
#end

UIViewController does not recognize method

please help me to resolve this issue
i have a view controller in a navigation stack named firstviewcontroller
FirstViewController.h
#class ImperialPickerController;
#class FractionPickerController;
#class MetricPickerController;
#interface FirstViewController : UIViewController {
UIView *pickerViewContainer;
ImperialPickerController *ImperialPickerController;
FractionPickerController *FractionPickerController;
MetricPickerController *MetricPickerController;
UIView *ImperialPickerViewContainer;
UIView *FractionPickerViewContainer;
UIView *MetricPickerViewContainer;
UISegmentedControl *segmentedControl;
NSInteger selectedUnit;
}
#property(nonatomic,retain) IBOutlet UIView *pickerViewContainer;
#property(nonatomic,retain) IBOutlet UIView *ImperialPickerViewContainer;
#property(nonatomic,retain) IBOutlet UIView *FractionPickerViewContainer;
#property(nonatomic,retain) IBOutlet UIView *MetricPickerViewContainer;
#property(nonatomic,retain) IBOutlet ImperialPickerController *ImperialPickerController;
#property(nonatomic,retain) IBOutlet FractionPickerController *FractionPickerController;
#property(nonatomic,retain) IBOutlet MetricPickerController *MetricPickerController;
#property(nonatomic,retain) IBOutlet UISegmentedControl *segmentedControl;
-(IBAction)toggleUnit;
#end
FirstViewController.m
#implementation FirstViewController
#synthesize ImperialPickerController;
#synthesize FractionPickerController;
#synthesize MetricPickerController;
#synthesize ImperialPickerViewContainer;
#synthesize FractionPickerViewContainer;
#synthesize MetricPickerViewContainer;
#synthesize pickerViewContainer;
#synthesize segmentedControl;
define METRIC_INDEX 0
define IMPERIAL_INDEX 1
define FRACTION_INDEX 2
-(IBAction)toggleUnit
{
selectedUnit = [segmentedControl selectedSegmentIndex];
if (selectedUnit == METRIC_INDEX)
{
[MetricPickerController updateLabel1];
}
}
#end
MetricPickerController.h
#interface MetricPickerController : NSObject <UIPickerViewDataSource,UIPickerViewDelegate> {
UIPickerView *pickerView;
UILabel *label;
}
#property(nonatomic,retain)UIPickerView *pickerView;
#property(nonatomic,retain)UILabel *label;
-(void)updateLabel1;
#end
MetricPickerController.m
import "MetricPickerController.h"
#implementation MetricPickerController
#synthesize pickerView;
#synthesize label;
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 2;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return 10;
}
-(void)updateLabel1
{
label.text = #"test"
}
the problem is that i get an error message on compiling here in the firstviewcontroller
-(IBAction)toggleUnit
{
selectedUnit = [segmentedControl selectedSegmentIndex];
if (selectedUnit == METRIC_INDEX)
{
[MetricPickerController updateLabel1]; <<<<< (MetricPickerController might not respond to +updateLabel1)!!
also if i click the toggle in IB xcode will crash with sigbart error
}
can anyone please help and advise what i have done wrong i think i have everything hooked up properly so i guess this is to do with my method declaration somehow
i know the code is incomplete at this stage but its driving me crazy trying to get rid of this error and i hope you can appreciate that i am just a learner
}
The problem is that updateLabel1 is an instance method, and you are sending it to a class (the +updateLabel1 instead of -updateLabel1 in the error message tells you this).
Since you've named your instance variables the same as the classes, you should be able to solve this by writing
[self.MetricPickerController updateLabel1];
instead - this makes it clear to the compiler you are referring to the instance variable and not the actual class.

Hiding UI Button After Pressed

Hey!
I am trying to hide a UIButton after it is pressed
This is my .h code:
#import <UIKit/UIKit.h>
#interface TemplateTestViewController : UIViewController { }
- (IBAction)ButtonPressed;
IBOutlet UIButton *sample; #property (nonatomic, retain) IBOutlet UIButton
*sample;
#end
This is my .m code:
#import "TemplateTestViewController.h"
#import "Page.h"
#implementation TemplateTestViewController
#synthesize sample;
-(IBAction) ButtonPressed{
if ( ([sample.selected=YES]))
{
sample.hidden = YES;
}
I get four errors and the code does not work. What am I doing wrong? I am new so some sample code would be much appreciated!
you have issue in the your .h file with #interface declaration. use below
#import <UIKit/UIKit.h>
#interface TemplateTestViewController : UIViewController {
IBOutlet UIButton *sample;
}
#property (nonatomic, retain) IBOutlet UIButton *sample;
- (IBAction)ButtonPressed;
#end
And in your .m class
-(IBAction) ButtonPressed {
if(sample.selected == YES)
{
sample.hidden = YES;
}
}

Sharing Data between XIB's

I'm following the James Brannan tutorial's, and im trying to share data between some xbis. No luck.
I have 2 xib's. The first, simple button and textfield. The second, just a label, to show the result of the first xib textfield.
So, i dont know what im doing wrong. Im using NSObject like in tutorial.
SharedData.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface SharedData : NSObject {
NSString *MeuNome;
}
#property (nonatomic, retain) NSString *MeuNome;
#end
SharedData.m
#import "SharedData.h"
#implementation SharedData
#synthesize MeuNome;
- (void) dealloc {
self.MeuNome = nil;
[super dealloc];
}
#end
FirstStepViewController.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "SharedData.h"
#interface FirstStepViewController : UIViewController {
IBOutlet SharedData *sharedData;
IBOutlet UITextField *campoNome;
}
#property (nonatomic, retain) UITextField * campoNome;
#property (nonatomic, retain) SharedData *sharedData;
- (IBAction) takeNextStep: (id) sender;
#end
FirstStepViewController.m
#import "FirstStepViewController.h"
#import "SecondStepViewController.h"
#import "LuconeAppDelegate.h"
#implementation FirstStepViewController
#synthesize campoNome, sharedData;
- (IBAction) takeNextStep : (id) sender{
// declaracao de shared data
[sender resignFirstResponder];
self.sharedData.MeuNome = self.campoNome.text;
// faz animacao para proximo slide
SecondStepViewController *varSecondViewController = [[SecondStepViewController
alloc] initWithNibName:#"SecondStepViewController" bundle:nil ];
[self.navigationController pushViewController:varSecondViewController
animated: YES];
[self navigationController].navigationBarHidden = NO;
}
- (void)viewDidLoad {
[self navigationController].navigationBarHidden = YES;
[super viewDidLoad];
}
- (void)dealloc {
self.sharedData = nil;
//self.campoNome = nil;
[super dealloc];
}
#end
SecondStepViewController.h
#import <UIKit/UIKit.h>
#import "SharedData.h"
#interface SecondStepViewController : UIViewController {
IBOutlet SharedData *sharedData;
IBOutlet UILabel *nome;
}
#property (nonatomic, retain) SharedData *sharedData;
#property (nonatomic, retain) UILabel *nome;
#end
SecondStepViewController.m
#import "SecondStepViewController.h"
#import "SharedData.h"
#implementation SecondStepViewController
#synthesize nome, sharedData;
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = #"step two";
self.nome.text = self.sharedData.MeuNome;
}
- (void)dealloc {
self.sharedData = nil;
[super dealloc];
}
#end
What's wrong?
Thanks!
There are three small problems with your code.
IBOutlet is only for controls that you put on your view using interface builder e.g. UIButtons, UILabels. So it is unnecessary for instances of SharedData to be IBOutlets. Furthermore if you create your interface programmatically then using IBOutlet is again unnecessary.
You Declare that sharedData is an instance of SharedData class but you do not instantiate it. in your FirstStepViewController.m before you set any of sharedData's properties you should add the following code:
sharedData = [[sharedData alloc] init];
after that you can do:
self.sharedData.MeuNome = self.campoNome.text;
if you omit the "self." the code should work just as fine.
Finally before pushing the second view controller to the navigation stack you have to assign the sharedData object in your first view controller to sharedData in your second view controller.
in your FirstStepViewController.m add:
[varSecondViewController sharedData] = [self sharedData];
before:
[self.navigationController pushViewController:varSecondViewController
animated: YES];
Finally make sure you have connected all your outlets correctly in interface builder and everything should run perfectly then :)

Implementing delegate methods for modal view controller data transfer

I have a simple project to present a modal view controller and transfer back a string based on which button in the modal VC that gets pressed. I based it all on watching the Stanford class on iTunes U. It looks like I have everything correct, but I get a couple of compiler warnings.
First I get one called passing argument 1 of 'setDelegate:' from incompatible pointer type in TransferViewController.m
Second I get four warnings called Invalid receiver type 'id <MyModalViewControllerDelegate>*' but these aren't displayed in the build results area, rather next to the offending lines in MyModalViewController.m, both lines in each of the button actions.
Here's the code...
// TransferViewController.h
#import <UIKit/UIKit.h>
#import "MyModalViewController.h";
#interface TransferViewController : UIViewController <MyModalViewControllerDelegate> {
UILabel *label;
UIButton *button;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#property (nonatomic, retain) UIButton *button;
- (IBAction)updateText;
#end
// TransferViewController.m
#import "TransferViewController.h"
#implementation TransferViewController
#synthesize label;
#synthesize button;
- (IBAction)updateText {
MyModalViewController *myModalViewController = [[MyModalViewController alloc] init];
myModalViewController.delegate = self; // I get the warning here.
[self presentModalViewController:myModalViewController animated:YES];
[myModalViewController release];
}
- (void)myModalViewController:(MyModalViewController *)controller didFinishSelecting:(NSString *)selectedDog {
label.text = selectedDog;
[self dismissModalViewControllerAnimated:YES];
}
#end
// MyModalViewController.h
#import <UIKit/UIKit.h>
#protocol MyModalViewControllerDelegate;
#interface MyModalViewController : UIViewController {
UIButton *abby;
UIButton *zion;
id <MyModalViewControllerDelegate> delegate;
}
#property (assign) id <MyModalViewControllerDelegate> delegate;
- (IBAction)selectedAbby;
- (IBAction)selectedZion;
#end
#protocol MyModalViewControllerDelegate <NSObject>
#optional
- (void)myModalViewController:(MyModalViewController *)controller didFinishSelecting:(NSString *)selectedDog;
#end
// MyModalViewController.m
#import "MyModalViewController.h"
#implementation MyModalViewController
#synthesize delegate;
- (IBAction)selectedAbby {
if ([self.delegate respondsToSelector:#selector (myModalViewController:didFinishSelecting:)]) {
[self.delegate myModalViewController:self didFinishSelecting:#"Abby"];
}
}
- (IBAction)selectedZion {
if ([self.delegate respondsToSelector:#selector (myModalViewController:didFinishSelecting:)]) {
[self.delegate myModalViewController:self didFinishSelecting:#"Zion"];
}
}
Get rid of those *s after id <something> and before delegate.
So make this
id <MyModalViewControllerDelegate> *delegate;
this
id <MyModalViewControllerDelegate> delegate;