iPhone - SubClassing UIToolbar the right way? - iphone

I created a new Class named
CustomToolbar
Then i created an empty nib, added a
toolbar to it, and set the toolbar
class to "CustomToolbar".
What is the proper way of initializing CustomToolbar In code so that my class uses the nib file?
I already have written the code to do that but i know it's not the correct way and it has a leak.
#interface CustomToolbar : UIToolbar {
}
#property (nonatomic, retain) IBOutlet UIBarButtonItem *button;
#end
#implementation CustomToolbar
- (id)initWithDelegate
{
NSArray *objects = [[NSBundle mainBundle]
loadNibNamed:#"CustomToolbar"
owner:nil
options:nil];
if (self = (CustomToolbar*) [objects objectAtIndex:0])
{
//do some work here
}
return self;
}
#end

The NIB will call initWithCoder: on your custom class, like this:
- (id)initWithCoder:(NSDecoder*)aDecoder {
self = [super initWithCoder:aDecoder];
if( self ) {
// Do something
}
return self;
}
If you really want to load it the way you do now, you need to retain the object returned from loadNibNamed.

Related

Can not pass value of textfield to other viewcontroller in ios

I want to pass value of textfield in LoginView to uploadview but UserLogin1 always is NULL ( 0x000000)
This is my codes:
loginvuew.h
#property (nonatomic,retain) IBOutlet UITextField *username;
loginview.m
-(IBAction)LoginButton:(id)sender{
uploadview= [[UploadTab alloc] initWithFrame:CGRectMake(0,0,0,0) andUsertring:username.text];
}
uploadview.h
#property (nonatomic, copy) NSString *UserLogin1;
- (id)initWithFrame:(CGRect)frame andUsertring:(NSString *)strUser;
uploadview.m
- (id)initWithFrame:(CGRect)frame andUsertring:(NSString *)strUser
{
if (self) {
UserLogin1=[[NSString alloc] init];
UserLogin1 = strUser;
}
return self;
}
- (void)viewDidLoad
{
NSLog(#"User login: %#",UserLogin1);
self.navigationItem.title = [NSString stringWithFormat: #"Hello, %#",UserLogin1];
}
WHen run in initWithFrame(), string passing correct but in ViewDidLoad is not correct. And UserLogin1 variable always is 0x00000. DO you have sugesstions? Thansk in advance
You are messing up things I think. What class is an UploadTab ? The view does not have a method viewDidload ! It's UIViewController's one. If it is ViewController then you should allocate it properly with initWithNibName:bundle or smth like that.
ADDED:
Your code missing actual alloc-init method
- (id)initWithFrame:(CGRect)frame andUsertring:(NSString *)strUser
{
// self is nil here! It is in almost every cases done like self = [[super alloc] initBlaBla]
if (self) {
UserLogin1=[[NSString alloc] init];
UserLogin1 = strUser;
}
return self;
}

Change UIView (inside nib) background from other viewcontroller

Anyone know how to alter a view inside a nib from another viewcontroller? I have a outlet from the view in nib-file to the view class-file and I have #synthesize the view-class .m file. And then I #import "InfoView.h" (which is the view class) the view to the viewcontroller and at last I:
InfoView *infoView;
if (infoView == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"infoView" owner:self options:nil];
infoView = [nib objectAtIndex:0];
}
infoView.contentView.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];"
But I can't get the background to change.
Has anyone tried to do something like this before?
I appreciate any input thank you!
EDIT:
Have addressed the UIColor issue but that was not the problem.
Try this code I have made the demo app for you.
create file CustomView.h
#import <UIKit/UIKit.h>
#interface CustomView : UIView
#property (nonatomic, strong) IBOutlet UILabel *titleLbl;
#end
CustomView.m. If you are using XIB
#import "CustomView.h"
#implementation CustomView
#synthesize titleLbl = _titleLbl;
- (id)initWithCoder:(NSCoder *)aDecoder
{
if(self = [super initWithCoder:aDecoder])
{
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"CustomView" owner:self options:nil];
UIView *theEditView = [nibObjects objectAtIndex:0];
theEditView.frame = self.bounds;
[self addSubview: theEditView];
theEditView = nil;
}
return self;
}
Set fileOwner of CustomView.XIB is CustomView. and connect outlets.
Where ever you want to use CustomView take a UIView object in your XIB, and rename UIView class with CustomView. Create an IBOutlet in your .h file and connect it with CustomView object in XIB.
Now do this:
self.customViewObj.backgroundColor = [UIColor redColor];
self.customViewObj.titleLbl.text = #"Prateek";
In your case your customview object is not created. if you print your object it will show you nil.
You're using the -colorWithRed:green:blue:alpha wrongly. They expect a float value between 0 and 1. You need to do:
infoView.contentView.backgroundColor = [UIColor colorWithRed:50.0/255.0 green:50.0/255.0 blue:50.0/255.0 alpha:1];"
If you are trying to change the background color.. then do this:
infoView.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];

Terminating app due to uncaught exception 'NSInvalidArgumentException'

I am not getting an error now but my delegate is not working. I am making a custom keyboard so i have the UIViewController and a UIView. I want the UIView to call the sendKeyboardShortCut method in the UIViewController. The sendKeyboardShortCut method is not being called. Thank You
//ViewController .h
#import "KeyboardExtension.h"
#interface PageViewController : UIViewController <UITextViewDelegate,sendKeyboardShortCutDelegate> {
KeyboardExtension *inputAccView;
}
-(void)sendKeyboardShortCut:(NSString*)shortCut;
#property (nonatomic, assign) IBOutlet UITextView *tv;
#end
//ViewController .m
#implementation PageViewController
#synthesize tv;
- (void)viewWillAppear:(BOOL)animated
{
tv.delegate = self;
}
-(void)createInputAccessoryView{
inputAccView = [[[KeyboardExtension alloc] init]autorelease];
inputAccView.delegate = self;
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"KeyboardExtension" owner:self options:nil];
inputAccView = [nibObjects objectAtIndex:0];
}
-(void)textViewDidBeginEditing:(UITextView *)textView{
[self createInputAccessoryView];
[textView setInputAccessoryView:inputAccView];
}
-(void)sendKeyboardShortCut:(NSString*)shortCut{
if ([shortCut isEqualToString:#"dash"] ) {
NSRange range = tv.selectedRange;
if((range.location+range.length)<=tv.text.length)
{
NSString * before = [tv.text substringToIndex:range.location];
NSString * after = [tv.text substringFromIndex:range.location+range.length];
tv.text = [NSString stringWithFormat:#"%#-%#",before,after];
}
}
}
#end
//keyboard view .h
#import <UIKit/UIKit.h>
#protocol sendKeyboardShortCutDelegate <NSObject>
-(void)sendKeyboardShortCut:(NSString*)shortCut;
#end
#interface KeyboardExtension : UIView
-(IBAction)dash:(id)sender;
#property(nonatomic,assign) id<sendKeyboardShortCutDelegate>delegate;
#end
//keyboard view .m
#import "KeyboardExtension.h"
#implementation KeyboardExtension
#synthesize delegate;
-(IBAction)dash:(id)sender{[delegate sendKeyboardShortCut:#"dash"];}
#end
I suspect that:
1) your button's target is the nib's file owner and not KeyboardExtension
2) your action is setup not to send the sender, so UIKit calls -comma instead of -comma:
Also, the following code is highly suspect:
inputAccView = [[[KeyboardExtension alloc] init]autorelease];
inputAccView.delegate = self;
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"KeyboardExtension" owner:self options:nil];
inputAccView = [nibObjects objectAtIndex:0];
A) you allocate an object that you barely use as you replace it immediately with the nib's object
B) the way to extract the object from the nib ([nibObjects objectAtIndex:0]) is really not robust. You should create an IBOutlet in PageViewController and link it in the nib
I think you should revisit the way you use nibs here.
A final (unrelated) point: why are you using [NSString stringWithFormat:#"..."] instead of just #"..."?

Best Practice: Presenting a Subclassed UIView (with own xib) as subview of UIViewControllers

My goal is, to have a subclassed UIView (lets call it infoView) designed in his own XIB so that I can present it in many UIViewController's.
The Problem:
So far, when I was adding UIView's to a UIViewController I always had to make an UIViewController the file's owner of the UIView's .xib file to load the view with something like:
...
//this is inside the calling UIViewController's method
// InfoView *infoView is ivar and a subclass of UIView
infoView = nil;
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:#"InfoView"
owner:self options:nil];
for (id object in bundle) {
if ([object isKindOfClass:[InfoView class]])
infoView = (InfoView *)object;
}
[[self view] addSubview:infoView];
...
But I want to use the same UIView in many different UIViewController's, so I actually don't want a file's owner except maybe the class itself. In ThomasM's question he was setting the UIView itself to be the file's owner but without success.
In the answers there I found a solution to set the file's owner to nil. To do so I had to add all calling UIViewController objects from the Interface Builder object library to the InfoView.xib file and connect them with their infoView outlets.
But this doesn't feel right. So here I would like to collect solutions to
encapsulate a UIView together with his xib-file to use it in many different view controllers. How do you guys handle that?
Thx for any help.
EDIT:
The infoView is something like an overlay which appears when the user presses a button on one of the view controllers. It's NOT the View controllers "main" view. It gives detailed informations about the view of his superviews view controller and will disappear afterwards. I only fill the infoView with different contents threw out all the calling view controllers.
Like Hollance answer was pointing out I am using UINib.
To use it, leave the .xib files owner nil and place all customization of the infoView inside the initWithCoder: method of your InfoView class implementation. This will get called if you obtain the InfoView.xib like:
// here InfoView is the name of the .xib file
UINib *infoNib = [UINib nibWithNibName:#"InfoView" bundle:nil];
NSArray *topLevelObjects = [infoNib instantiateWithOwner:self options:nil];
QInfoView *infoView = [topLevelObjects objectAtIndex:0];
So you want to load a UIView from a nib that you wish to use in more than one UIViewController, and you want to connect it to an outlet on each of those view controllers. Is that correct?
Then make a UIViewController subclass (let's call it FakeViewController) with an IBOutlet property. Set that FakeViewController as the nib's File's Owner and connect your UIView to its outlet.
Done.
You just need to make sure all your other view controllers also have these outlet properties (although they don't need to be IBOutlets), but the nib loader doesn't actually check to make sure the class that you pass into the owner parameter equals the class name you specified in Interface Builder. So you can fake it.
Oh, and if you're OS 4.0 and higher, use UINib to load the nib file.
And yet another way is to create your own "controller" based on NSObject to define your own life-circle (instead of standard UIViewController life-circle).
For example:
BaseSubview.h:
#interface BaseSubview : NSObject {
UIView* _view;
}
#property (nonatomic, retain) IBOutlet UIView* view;
- (void)myMethod;
#end
BaseSubview.m:
#import "BaseSubview.h"
#implementation BaseSubview
#synthesize view = _view;
- (id)init
{
self = [super init];
if (self) {
// ...
}
return self;
}
- (void)dealloc
{
[_view removeFromSuperView];
[super dealloc];
}
- (void)myMethod
{
// view specific logic here
_view.backgroundColor = [UIColor redColor];
}
#end
InfoView.h:
#import "BaseSubview"
#interface InfoView : BaseSubview {
UILabel* _labelInfo;
}
#property (nonatomic, retain) IBOutlet UILabel* labelInfo;
#end
InfoView.m:
#import "InfoView.h"
#implementation InfoView
#synthesize labelInfo = _labelInfo;
- (id)init
{
self = [super init];
if (self) {
// ...
}
return self;
}
- (void)myMethod
{
// view specific logic here
_labelInfo.text = #"current time...";
[super myMethod];
}
#end
InfoView.xib:
file owner is InfoView
assign of outlets as usual
view is parent all other controls (such as labels, etc)
HugeAndComplicatedViewController.h:
// ...
// among other var definitions
InfoView* _infoView;
// ...
HugeAndComplicatedViewController.m, most interesting part:
// when you decide to show your view
// probably in loadView
_infoView = [[InfoView alloc] init];
[[NSBundle mainBundle] loadNibNamed:#"InfoView" owner:_infoView options:nil];
[self.view addSubview:_infoView.view];
// possibly perform specific logic
[_infoView myMethod];
// no need sub-view any more
// probably in dealloc
[_infoView release];
So now you have your own sub-view with logic and design separated from "Huge & Complicated" view-controller. It can have any life-circle you need for your current project.
does infoView need to be a subview?
in your viewController:
-(id) init {
self = [super initWithNibName:#"myNib" bundle:nil];
if (self) {
// code here
}
}

iOS using External Object in Nib

I am writing an application that needs to interfaces with different backend systems. I decided to use a Protocol in order to abstract my backend Classes. I created a nib called LoginViewController that contains an "External Object" reference of type "NSObject", and wired it to the systemDelegate outlet in my LoginViewController.
#interface LoginViewController : UIViewController {
}
#property (nonatomic, retain) IBOutlet UITextField *usernameTextView;
#property (nonatomic, retain) IBOutlet UIImageView *captchaImageView;
#property (nonatomic, retain) IBOutlet UITextField *captchaTextView;
#property (nonatomic, retain) IBOutlet NSObject <BackEndSystemDelegate> *systemDelegate;
- (IBAction) submitCaptcha:(id) sender;
- (IBAction)dismissKeyboard: (id)sender;
- (IBAction) animateViewUp: (id) sender;
- (IBAction) animateViewDown: (id) sender;
- (void) animateViewOnYAxis: (int) offset;
- (void) loadCaptchaImage;
#end
I instanciate the LoginViewController in my application delegate, then try to load the nib with the external object reference. My code calls the loadNibNamed and crashes without a stack trace. I do not reach the NSLog statements after the invocation:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSObject <BackEndSystemDelegate> *systemDelegate = [[ACMEBackEndSystemDelegate alloc] init];
// Init LoginView, and load nib with systemDelegate
self.viewController = [[LoginViewController alloc] init];
NSDictionary *proxies = [NSDictionary dictionaryWithObject:systemDelegate forKey:#"systemDelegate"];
NSDictionary *options = [NSDictionary dictionaryWithObject:proxies forKey:UINibExternalObjects];
NSArray *toplevelobjects = [[NSBundle mainBundle] loadNibNamed:#"LoginViewController"
owner:self.viewController
options:options];
if (toplevelobjects) {
NSLog(#"toplevelobjects is nil");
} else {
NSLog(#"toplevelobjects count %d", [toplevelobjects count]);
}
NSLog(#"Controller: %#, View: %#", viewController, viewController.view);
// Add the view controller's view to the window and display.
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
I am at a loss trying to figure this out. Any help would be appreciated.
Thanks,
J Garcia
A few comments:
Generally you refer to a delegate object using the pattern id<SomeProtocolName> and not NSObject<SomeProtocolName>
You alloc your systemDelegate variable but never release it. This is a memory leak.
If the ACMEBackEndSystemDelegate class implements the BackEndSystemDelegate protocol, it is sufficient to allocate it like ACMEBackEndSystemDelegate* systemDelegate = [[ACMEBackEndSystemDelegate alloc] init];
Now, as for your crash, you said there's a crash on this line:
NSArray *toplevelobjects = [[NSBundle mainBundle] loadNibNamed:#"LoginViewController"
owner:self.viewController
options:options];
I assume you have a .xib called LoginViewController.xib. Open it up. That is the class type set for "File's Owner"? Is it LoginViewController? If not, set it. Now check the view outlet property. Is it set to a UIView in the .xib (perhaps a top-level UIView object)? If not, set it.