iphone - passing an object on an UIToolbarButton action - iphone

Is that possible to make a UIToolbarButton pass an object to its target by using some exoteric method (as it seems not to be possible using regular button use)?
I mean something like
UIBarButtonItem *Button = [[UIBarButtonItem alloc] initWithImage:buttonImage
style:UIBarButtonItemStylePlain target:self action:#selector(doSomething:) **withObject:usingThis**];
I know I can trigger a method that will launch the full method with the object, but for the sake of elegance I was trying to minimize the code... I suspect it is not possible, but as you guys out there are insanely good you may come with an transcendental answer... who knows...

You would have to extend the UIBarButtonItem class.
Here is an example creating RCBarButtonItem class. I've used initWithTitle for ease, I'm sure you could change it...
The UIBarButtonItem Subclass
#import <UIKit/UIKit.h>
#interface RCBarButtonItem : UIBarButtonItem {
id anObject;
}
#property (nonatomic, retain) id anObject;
- (id)initWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style target:(id)target action:(SEL)action withObject:(id)obj;
#end
#implementation RCBarButtonItem
#synthesize anObject;
-(void)dealloc {
[anObject release];
[super dealloc];
}
- (id)initWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style target:(id)target action:(SEL)action withObject:(id)obj {
if (self = [super initWithTitle:title style:style target:target action:action]) {
self.anObject = obj;
}
return self;
}
#end
Then this could then be implemented like so:
#import "RootViewController.h"
#import "RCBarButtonItem.h"
#implementation RootViewController
- (void)viewDidLoad {
[super viewDidLoad];
RCBarButtonItem *button = [[RCBarButtonItem alloc] initWithTitle:#"Hello"
style:UIBarButtonItemStylePlain
target:self
action:#selector(doSomething:)
withObject:#"Bye"];
self.navigationItem.rightBarButtonItem = button;
}
- (void)doSomething:(id)sender {
NSLog(#"%#", [(RCBarButtonItem *)sender anObject]);
}

What I've done in this situation is create an NSDictionary property called, say, buttonArguments:
self. buttonArguments = [[NSDictionary alloc] initWithObjectsAndKeys: usingThis, Button, ... , nil];
Then in your doSomething: method, look up the object based on the sender parameter.

I prefer using categories:
UIBarButtonItem+BarButtonItem.h
#interface UIBarButtonItem (BarButtonItem)
#property (strong, nonatomic) NSDictionary *userInfo;
#end
UIBarButtonItem+BarButtonItem.m
static void *kUserInfo = &kUserInfo;
#implementation UIBarButtonItem (BarButtonItem)
- (NSDictionary *)userInfo {
return objc_getAssociatedObject(self, kUserInfo);
}
- (void)setUserInfo:(NSDictionary *)userInfo {
objc_setAssociatedObject(self, kUserInfo, userInfo,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end

Related

Protocol method does not get invoked, shows delegate 'nil'

I'm working on an iPad App and i'm having issue with delegate... the protocol method does not get invoked. i'm not sure what i'm missing, here is my code.
#protocol pickerLabelProtocol <NSObject>
- (void)selectedPickerData:(UILabel *)sender;
#end
#interface showPickerVC : UIViewController
#property (nonatomic, strong) id <pickerLabelProtocol> delegate;
#end
#implementation showPickerVC
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
//i used breakpoint, the 'delegate' is always nil for some reason?
[self.delegate selectedPickerData:self.mainLabel];
}
----------------------
#interface someViewController : UIViewController <pickerLabelProtocol>
#property (nonatomic, strong) showPickerVC *showPicker;
#end
#implementation someViewController
- (void)selectedPickerData:(UILabel *)sender
{
//protocol method
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.showPicker = [[showPickerVC alloc]init];
self.showPicker.delegate = self;
}
I can not got any mistake From your code but i suggest you that Be clear about when you create object of showPickerVC add it's delegate self
Such Like ,
showPickerVC *obj = [[showPickerVC alloc] init];
obj.delegate = self; /// YOur protocol delegate
.
.
[self presentModalViewController:obj animated:YES];
And Also add code as following
#implementation showPickerVC
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if([self.delegate respondsToSelector:#selector(selectedPickerData:)])
{
[self.delegate selectedPickerData:self.mainLabel];
}
}
For More information about How to create/use of Protocol.

custom UISwitch won't change state

I have a custom uiswitch class, nothing special, the header looks like this:
#import <Foundation/Foundation.h>
#class Course;
#class Student;
#interface AttandanceSwitch : UISwitch
#property (strong,nonatomic) Course *course;
#property (strong,nonatomic) Student *student;
#property (strong,nonatomic) UISwitch *originalSwitch;
-(id)initWithSwitch:(UISwitch *)newSwitch Student:(Student *)student andCourse:(Course *)course;
-(NSString *) description;
#end
the .m file looks as follows:
#import "AttandanceSwitch.h"
#import "Course.h"
#import "Student.h"
#implementation AttandanceSwitch
#synthesize course,student,originalSwitch;
-(id)initWithSwitch:(UISwitch *)newSwitch Student:(Student *)studentP andCourse:(Course *)courseP
{
self = [super init];
self.course = courseP;
self.student = studentP;
self.originalSwitch = newSwitch;
[self.originalSwitch setOn:YES];
return self;
}
-(NSString *) description
{
return [NSString stringWithFormat:#"Student: %#, Course: %#, Switch: %#",student.name,course.name,[originalSwitch description]];
}
#end
So whats actually the problem - when I create a new object of this class using initWithSwitch, I cant set the originalSwitch value. I even tried to set it statically (see self.originalSwitch setOn:YES above), but even if I do that, the switch is still off.
Any ideas on how to get this working would be very appreciated...
this is a excerpt from usage of this class in tableviewcell:
UISwitch *switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
[switchView addTarget:self action:#selector(valueChanged:) forControlEvents:UIControlEventTouchUpInside];
AttandanceSwitch *customSwitch = [[AttandanceSwitch alloc] initWithSwitch:switchView Student:student andCourse:courseToTrack];
Student and Course get filled allright.
Your code will work if you call it like this in your ViewController's viewDidLoad:
UISwitch *newSwitch = [[UISwitch alloc] init];
AttandanceSwitch *customSwitch = [[AttandanceSwitch alloc] initWithSwitch:newSwitch];
[self.view addSubView:customSwitch.original];
[customSwitch.original setOn:YES];

Subclassed controls not being released

We have found a large memory issue in our application. We have subclassed UITextField and add these to all of our main views. The main views are being dealloced correctly, however the dealloc method in our subclass never gets hit. Here is our subclass:
Header:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "MyEntities.h"
#import "MyControlHelper.h"
#interface MyTextField : UITextField {
MyControlHelper *myHelper;
UIView *disabledEffect;
}
#property (nonatomic, retain) MyControlHelper *myHelper;
#property (nonatomic, retain) UIView *disabledEffect;
#end
Implementation:
#import "MyTextField.h"
#implementation MyTextField
#synthesize myHelper;
#synthesize disabledEffect;
-(id) initWithCoder:(NSCoder *)aDecoder{
if (self = [super initWithCoder:aDecoder]){
myHelper = [[MyControlHelper alloc] init];
[myHelper setBoundTextField:self];
[myHelper SetupKeyboardListener];
[self setReturnKeyType:UIReturnKeyDone];
self.autocorrectionType = FALSE;
self.delegate = myHelper;
}
return self;
}
-(id) init{
if (self = [super init]){
myHelper = [[MyControlHelper alloc] init];
[myHelper setBoundTextField:self];
[myHelper SetupKeyboardListener];
[self setReturnKeyType:UIReturnKeyDone];
self.autocorrectionType = FALSE;
self.delegate = myHelper;
}
return self;
}
-(id)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]){
myHelper = [[MyControlHelper alloc] init];
[myHelper setBoundTextField:self];
[myHelper SetupKeyboardListener];
[self setReturnKeyType:UIReturnKeyDone];
self.autocorrectionType = FALSE;
self.delegate = myHelper;
}
return self;
}
-(void)dealloc{
self.myHelper = nil;
self.disabledEffect= nil;
[super dealloc];
}
#end
Any help would be greatly appreciated.
Cheers.
You might have an issue with your myHelper reference. You have declared it as (nonatomic, retain) property. If your MyControlHelper class has a property for your MyTextField also being retained, you are building a cyclic reference and your field is retaining myHelper and vice versa.
If this is the case I suggest you declare the property for the textfield within MyControlHelper with (nonatomic, assign) to resolve the cycle.
It would help to post the code for MyControlHelper, as well.

Changed nib File to a OPGL ES View, Buttons don't work

Hy,
There is no problmen with changing the nib but when I'm in the nib-File with the OPGL ES view my buttons that are integrated in this view won't work. When I press one the application crashes.
This is the Code how I change the View to the Second Nib:
- (IBAction)drawEAGLView:(id)sender {
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"settingsViewController" owner:self options:nil];
EAGLView* glView = [ nibViews objectAtIndex: 1];
self.view = glView;
[glView startAnimation];}
The Animation is working .
This is the Code of the ViewController where the Button is declared:
SettingsViewController.h:
#import <UIKit/UIKit.h>
#interface SettingsViewController : NSObject {
IBOutlet UIButton *wowButton;
}
#property(nonatomic,retain) IBOutlet UIButton *wowButton;
- (IBAction)wowButtonPressed:(id)sender;
int testvarAnimation;
int animNext;
#end
SettingsViewController.m:
#import "SettingsViewController.h"
#implementation SettingsViewController
#implementation SettingsViewController
#synthesize wowButton;
- (IBAction)wowButtonPressed:(id)sender{
NSLog(#"TOUCHED");
//testvarAnimation = 1;
animNext =1;
}
- (void)dealloc {
[wowButton release];
[super dealloc];
}
#end
Here I show you how I connected the Functions and the Outlets:
http://s3.imgimg.de/uploads/screenshotConna91ea645png.png
Please tell me If you need more informations.
Thanks
Hy I solved my problem, I rewrote my drawEAGLView Methode:
- (IBAction)drawEAGLView:(id)sender {
SettingsViewController *sViewController = [[SettingsViewController alloc] initWithNibName:#"SettingsViewController" bundle:nil];
[[self.view superview] addSubview:sViewController.view];
}
I also corrected my SettingsViewController.h:
#import <UIKit/UIKit.h>
#class EAGLView;
#interface SettingsViewController : UIViewController {
IBOutlet UIButton *wowButton;
EAGLView *glview;
}
#property(nonatomic,retain) IBOutlet UIButton *wowButton;
#property (nonatomic, retain) IBOutlet EAGLView *glView;
- (IBAction)wowButtonPressed:(id)sender;
int testvarAnimation;
int animNext;
#end
And last but not least I changed my SettingsViewController.m:
#import "SettingsViewController.h"
#import "EAGLView.h"
#implementation SettingsViewController
#synthesize wowButton, glView;
- (IBAction)wowButtonPressed:(id)sender{
NSLog(#"TOUCHED");
//testvarAnimation = 1;
animNext =1;
}
- (void)viewDidLoad {
glView.animationInterval = 1.0 / 60.0;
[glView startAnimation];
[super viewDidLoad];
}
- (void)dealloc {
[wowButton release];
[super dealloc];
}
#end
I also changed my Nib file. I removed the ViewController and set the type of the File's Owner to my viewController named "SettingsViewController". I connected the File's Owner Outlet view with the EAGLView and the button with the SettingsViewControllers wowButtonPressed Method
I hope I could help also someone else,
Thanks
Here is the right solution for this problem:
First you have to create a parnet object of your view you want to be shown as first.
You need to do this in the header file of the second view.
//
// SettingsViewController.h
// NeheTutorial2
//
// Created by Global Sound on 05.04.11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
#class EAGLView;
#class mobilesynthViewController;
#interface SettingsViewController : UIViewController {
IBOutlet UIButton *wowButton;
IBOutlet UIButton *optiButton;
EAGLView *glView;
//mobilesynthViewController *mViewController;
mobilesynthViewController *parent;
}
#property(nonatomic,retain) IBOutlet UIButton *wowButton;
#property(nonatomic,retain) IBOutlet UIButton *optiButton;
#property (nonatomic, retain) IBOutlet EAGLView *glView;
//#property(nonatomic,retain) IBOutlet mobilesynthViewController *mViewController;
#property (nonatomic, retain) mobilesynthViewController *parent;
- (IBAction)wowButtonPressed:(id)sender;
- (IBAction)optiButtonPressed:(id)sender;
int testvarAnimation;
int animNext;
int musicOn;
int isAnimating;
float zaehler;
#end
Now in the implemetaion File you need to add the parent object to your function:
#import "SettingsViewController.h"
#import "EAGLView.h"
//#import "mobilesynthViewController.h"
#implementation SettingsViewController
#synthesize wowButton, optiButton, glView;//,mViewController;
#synthesize parent;
- (IBAction)wowButtonPressed:(id)sender{
NSLog(#"TOUCHED");
[parent startThread];
[parent showbButtonStop];
testvarAnimation = 0;
animNext =1;
//musicOn =1;
}
- (IBAction)optiButtonPressed:(id)sender{
NSLog(#"OPTIONS ON");
[self dismissModalViewControllerAnimated:YES];
[parent showbButton];
//mobilesynthViewController *aRootViewController = [[mobilesynthViewController alloc] initWithNibName:#"mobilesynthViewContoller" bundle:nil];
//[self setRootViewController:aRootViewController];
//[aRootViewController release];
//mobilesynthViewController *mViewController = [[mobilesynthViewController alloc] initWithNibName:#"mobilesynthViewController" bundle:nil];
//[[self.view superview] addSubview:mViewController.view];
//self.view = mViewController;
//CGRect rect = [[UIScreen mainScreen] applicationFrame];
//mViewController = [[mobilesynthViewController alloc] initWithFrame:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height)];
//self.view = mViewController;
}
- (void)viewDidLoad {
glView.animationInterval = 1.0 / 60.0;
[glView startAnimation];
[super viewDidLoad];
}
/*
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
*/
- (void)dealloc {
[wowButton release];
[optiButton release];
[super dealloc];
}
#end
In my case I'm calling a other function in the main View of my program. When the button in the second view is pressed the function in the first view is called through the the parent object
[parent startThread];
Again the startThread methode was declerated in another view.
I hope I could help you.

Calling navigation controller method from background operation

I'm running a background operation using NSOperation, which gets kicked off from my AppDelegate. What I want to do is when the operation is complete, have it call a method in the RootViewController.m file.
Currently there's a property in my AppDelegate named navigationController, and I"ve been trying to set up the call like this:
AppDelegate.m:
GetRSSOperation *gro = [[GetRSSOperation alloc] initWithURL:rssURL target:self selector:#selector(dataSourceDidFinishLoadingNewData:)];
[queue addOperation:gro];
[gro release];
GetRSSOperation.m:
[target performSelectorOnMainThread:selector withObject:alerts waitUntilDone:YES];
I have tried setting the target to self, self.navigationController, self.navigationController.view but none work.
I am aware that the issue might be due to my not yet having a complete understanding of the entire ios architecture, so would appreciate any pointers please.
**Adding more code to explain:
AppDelegate.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#interface AppDelegate : NSObject {
NSOperationQueue *queue;
UIWindow *window;
UINavigationController *navigationController;
NSURL *rssURL;
// storage for the alert list from RSS feed
NSMutableArray *alerts;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
#property (nonatomic, retain) NSMutableArray *alerts;
#property (nonatomic, retain) NSURL *rssURL;
#property (retain) NSOperationQueue *queue;
+ (id)shared;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "RootViewController.h"
#import "AlertViewController.h"
#import "RefreshTableHeader.h"
#import "GetRSSOperation.h"
#implementation AppDelegate
static AppDelegate *shared;
#synthesize window, navigationController, alerts;
- (id)init
{
if (shared) {
[self autorelease];
return shared;
}
if (![super init]) return nil;
queue = [[NSOperationQueue alloc] init];
shared = self;
return self;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Add the navigation controller's view to the window and display.
UIImageView *imageview=[[UIImageView alloc]initWithFrame:CGRectMake(270, 3, 37, 37)];
imageview.image=[UIImage imageNamed:#"iphone.png"];
[self.navigationController.navigationBar addSubview:imageview];
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
if (rssURL == nil) {
NSString *alertAddress = #"http://www.compoundstockearnings.com/com/public/selections.cfc?method=getrss&email=";
alertAddress = [alertAddress stringByAppendingString:cseLogin];
alertAddress = [alertAddress stringByAppendingString:#"&password="];
alertAddress = [alertAddress stringByAppendingString:csePass];
rssURL = [[NSURL alloc] initWithString:alertAddress];
}
GetRSSOperation *gro = [[GetRSSOperation alloc] initWithURL:rssURL target:self selector:#selector(dataSourceDidFinishLoadingNewData:)];
[queue addOperation:gro];
[gro release];
return YES;
}
return NO;
}
#end
GetRSSOperation just does the rss call, puts it in memory then tries to call the main navigation controller:
[target performSelectorOnMainThread:selector withObject:alerts waitUntilDone:YES];
The main navigation controller upon which I wish the thing to execute a selector, looks like this:
RootViewController.h
#import <UIKit/UIKit.h>
#interface RootViewController : UITableViewController {
IBOutlet RefreshTableHeader *alertTable;
UIImageView *imageView;
}
#end
RootViewController.m:
#import "RootViewController.h"
#import "AppDelegate.h"
#implementation RootViewController
- (void)dataSourceDidFinishLoadingNewData:(NSMutableArray *)tempAlerts
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.alerts = tempAlerts;
reloading = NO;
[alertTable flipImageAnimated:NO];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:.3];
[self.tableView setContentInset:UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f)];
[alertTable setStatus:kPullToReloadStatus];
[alertTable toggleActivityView:NO];
[UIView commitAnimations];
[popSound play];
}
Obviously I've shortened it a bit but that's the core of it. I just want the GetRSSOperation to be able to call RootViewController.m - dataSourceDidFinishLoadingNewData. any input appreciated.
It sounds like you're having a bit of trouble figuring out just how to format the call to performSelectorOnMainThread:withObject:waitUntilDone: correctly. It's pretty simple, really.
If you would normally call the method like this:
[target method:alerts]
you format the performSelector call like this:
[target performSelectorOnMainThread:#selector(method:) withObject:alerts waitUntilDone:YES];