I've been working on putting together a simple program that will display user information in an MVC model. The application works perfectly when all the data is on the View Controller, however, when attempting to move the data to a Profile model, the application will successfully build, however, no information will show up. Here is the code that I'm working with right now.
The Profile View Controller header:
DefaultProfileViewController.h
#import <UIKit/UIKit.h>
#import "Profile.h"
#interface DefaultProfileViewController : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *fullNameLabel;
#property (nonatomic, strong) Profile *profile;
#end
The Profile View Controller Implementation
#import "DefaultProfileViewController.h"
#class Profile;
#interface DefaultProfileViewController ()
#end
#implementation DefaultProfileViewController
#synthesize profile = _profile;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
_fullNameLabel.text = _profile.fullName;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
The Profile model header
#import <Foundation/Foundation.h>
#interface Profile : NSObject
#property (nonatomic, copy) NSString *fullName;
- (void)loadProfile:(NSString *)fullName;
#end
And the Profile model implementation
#import "Profile.h"
#implementation Profile
#synthesize fullName = _fullName;
- (void)loadProfile:(NSString *)fullName
{
_fullName = #"Full Name";
}
#end
As mentioned earlier, if the _fullName = #"Full Name" portion is used in the controller, this works no problem, and the text Full Name will display in the simulator. If the Profile model is used, the application will build, no errors or warnings, yet no information is displayed. I'm sure I'm overlooking something extremely simple, so any help would be greatly appreciated. Thanks.
You code sample declares the property profile but nowhere do you actually set the profile.
Remember that declared properties in Objective-C 2.0 do one thing - which is to create the accessor methods for you "behind your back." (In the "old days" we had to write accessor methods by hand. Laborious; but you got very good at memory management!)
In your case, likely whatever class that instantiates DefaultProfileViewController needs to create a Profile object and the call setProfile on the new DefaultProfileViewController instance.
Something like this:
// create Profile object
Profile *aProfile = [[Profile alloc] init];
[aProfile loadProfile:#"John Doe"];
DefaultProfileViewController *vc = [[DefaultProfileViewController alloc] initWithNibName:#"your nib name" bundle:nil];
// set the profile on your view controller
vc.profile = aProfile;
// add your view controller's view to the view hierarchy
// however you are doing that now...
Note, assumes ARC.
Related
I am a newbie to iPhone development and have some basic questions to ask about protocols and delegates. I have two view controllers: view controller and viewcontroller2nd. I have UITextField in one of them and would like to type something (like a name) in it and in the viewcontroller2nd, I have a UILabel and i would like it to appear Hello, name when the UITextField is changed.
I am following this video: http://www.youtube.com/watch?v=odk-rr_mzUo to get the basic delegate to work in a single view controller.
I am using protocols to implement this:
SampleDelegate.h
#import <Foundation/Foundation.h>
#protocol ProcessDelegate <UITextFieldDelegate>
#optional
- (BOOL)textFieldShouldReturn:(UITextField *)textField;
#end
#interface SampleDelegate : NSObject
{
id <ProcessDelegate> delegate;
}
#property (retain) id delegate;
#end
SampleDelegate.m
#import "SampleDelegate.h"
#implementation SampleDelegate
#synthesize delegate;
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
lbl.text = [NSString stringWithFormat:#"Hello, %#",txtField.text];
[txtField resignFirstResponder];
}
#end
ViewController.h
#import <UIKit/UIKit.h>
#import "SampleDelegate.h"
#interface ViewController : UIViewController <ProcessDelegate>
{
IBOutlet UITextField *txtField;
}
#end
Viewcontroller.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
ViewController2nd.h
#import <UIKit/UIKit.h>
#interface ViewController2nd : UIViewController <ProcessDelegate> {
IBOutlet UILabel *lbl;
}
#end
and ViewController2nd.m is standard code from Xcode.
My question is how do i link my delegate function to my viewcontroller and viewcontroller2nd to get it working?
Pardon me if the question is stupid.. Need some guidance. Do point me any other mistakes that i am doing as well.. Thanks..
Your delegation is a bit... Off.
Firstly: Don't override UIKit delegate methods through protocol inheritance. It's pointless. Why not just make your class conform to the specified delegate in the first place?
#protocol ProcessDelegate //No more protocol inheritance!
//...
#end
Secondly: When an object has defined a protocol, a valid instance of that object must be in use by its delegate (or at least passed to it). So, anything that wants to be the delegate of SampleDelegate (really a bad name for a class, by the way) would initialize a valid SampleDelegate object, and call -setDelegate: as though it were any other property.
//#import "SampleDelegate"
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//make this a property, so it isn't crushed when the function exits.
SampleDelegate *myDelegateObject = [[SampleDelegate alloc]init];
[myDelegateObject setDelegate:self]; //conform to the delegate
}
Thirdly: You don't actually define any delegate methods! What's the point of delegation if there's nothing to delegate!l
#protocol ProcessDelegate
-(void)someMethod;
#end
Fourth, and most important: Never, ever, ever, ever use the retain, or strong storage specifiers with a delegate! Delegate objects are supposed to be weak or assign to prevent nasty retain cycles.
#property (assign, nomatomic) id delegate;
I'm a relatively new iPhone developer and am making great progress building my 2nd iPhone app. In the app I'm building now I'm doing some code separation with some protocols and delegates so that I car re-use some of my code in a variety of places throughout my code.
Here's what I want to happen:
CITRootViewController creates an instance of a CITReportCreator class, passing itself as a property so that the reportCreator can open additional view controllers and such.
CITReportCreator class is declared as implementing the CITImageCaptureDelegate protocol, which is declared in the CITImageCaptureViewController file.
CITImageCaptureViewController defines the delegate protocol and has a method that passes back data and references to the child view controller so that CITReportCreator can interact with it's data, close the related XIB, etc.
I believe I'm getting the delegate and protocol established correctly, and verified that my 'delegate' object still contains data when it is called, but I'm getting a EXC_BAD_ACCESS method when my view controller tries to pass data back to the delegate in this line of code:
[self.delegate childViewControllerDidFinish:self];
Here's a good portion of the rest of my code. I had this working by using CITRootViewController as my delegate instead of the CITReportCreator class, but now that I'm separating the code, something has broke.
CITReootViewController.m (the view controller that calls the Report Creator)
//create a nrew report
-(IBAction)createReport:(id)sender {
CITReportCreator *report = [CITReportCreator alloc];
[report createNewReport:self];
}
CITReportCreator.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "CITImageCaptureViewController.h"
#interface CITReportCreator : NSObject <CITImageCaptureDelegate>
#property (strong, nonatomic) NSArray *imageList;
#property (nonatomic) NSInteger imageIndex;
-(int) createNewReport:(UIViewController *)parent ;
//Delegate Methods
-(void) childViewControllerDidFinish:(UIViewController*)viewController;
#end
And CITReportCreator.m
#import "CITReportCreator.h"
#implementation CITReportCreator
{
UIViewController *parentController;
}
#synthesize imageList;
#synthesize imageIndex;
-(int) createNewReport:(UIViewController *)parent
{
//store a reference to the parent view controller
parentController = parent;
// init code....
//head to the first image capture view
[self startImageCapture];
return 0;
}
-(int)startImageCapture
{
//pull the image name from the array of images
NSString *imageName = [imageList objectAtIndex:imageIndex];
//prep the image capture controller
CITImageCaptureViewController *capture = [[CITImageCaptureViewController alloc] initWithNibName:#"CITImageCaptureViewController" bundle:nil];
//Assign the capture controller's delegate
capture.imageName = imageName;
capture.delegate = self;
//Display the capture controller
[parentController presentModalViewController:capture animated:YES];
return 0;
}
//a break point set here never gets hit.
-(void) childViewControllerDidFinish:(UIViewController*)viewController;
{
[viewController dismissModalViewControllerAnimated:YES];
}
#end
And finally, the CITImageCaptureViewControllers
CITImageCaptureViewController.h
#import <UIKit/UIKit.h>
#protocol CITImageCaptureDelegate <NSObject>
-(void) childViewControllerDidFinish:(UIViewController*)viewController;
#end
#interface CITImageCaptureViewController : UIViewController
{
id<CITImageCaptureDelegate> delegate;
}
#property (nonatomic,assign) id<CITImageCaptureDelegate> delegate;
#property (nonatomic, assign) NSString *imageName;
//continue button pressed method
-(IBAction)continueButtonPressed:(id)sender;
#end
And the .m file
#import "CITImageCaptureViewController.h"
#interface CITImageCaptureViewController ()
#end
#implementation CITImageCaptureViewController
#synthesize navItem;
#synthesize imageName;
#synthesize delegate = _delegate; //i think this may be part of the problem
//cutting out initWithNibName, viewDidLoad, etc...
- (IBAction)continueButtonPressed:(id)sender
{
[self.delegate childViewControllerDidFinish:self];
}
#end
I find nothing with delegates and protocols all that simple, but I'm guessing I'm missing a small change somewhere. Can you help me head in the right direction?
I wanted to have the tab bar at the top.
So i created a new project in XCode. Added a view and then inside that view i added (scrollbar, text and another view). See picture.
What i wanted was to have my tab bar at the top. Then in the middle would be the contents from the tab bar and below it a small copyright text. See picture.
No idea how to make this correctly. I have tried to create the UITabBarController on the fly and then assign it into the view at the top. (Top white space on the picture dedicated for the tab bar).
Here is my code to init the MainWindow.
MainWindow.h
#import <UIKit/UIKit.h>
#class Intro;
#interface MainWindow : UIViewController
#property (strong, nonatomic) IBOutlet UIScrollView *mainContentFrame;
#property (strong, nonatomic) IBOutlet UIView *mainTabBarView;
#property (strong, nonatomic) UITabBarController *mainTabBar;
#property (nonatomic, strong) Intro *intro; // Trying to get this tab to show in the tab bar
#end
MainWindow.m
#import "MainWindow.h"
#import "Intro.h"
#interface MainWindow ()
#end
#implementation MainWindow
#synthesize mainContentFrame = _mainContentFrame;
#synthesize mainTabBarView = _mainTabBarView;
#synthesize mainTabBar = _mainTabBar;
#synthesize intro = _intro;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
_intro = [[Intro alloc] init];
NSArray *allViews = [[NSArray alloc] initWithObjects:_intro, nil];
[super viewDidLoad];
// Do any additional setup after loading the view.
_mainTabBar = [[UITabBarController alloc] init];
[_mainTabBar setViewControllers:allViews];
[_mainTabBarView.window addSubview:_mainTabBar.tabBarController.view];
}
- (void)viewDidUnload
{
[self setMainTabBar:nil];
[self setMainContentFrame:nil];
[self setMainContentFrame:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
What am i missing to get this to work? Wanted the content to end up in the scrollview so that all tabs are scrollable.
iOS User Interface Guidelines say that the UITabBar has to be at the bottom of the ViewController. You should either create your own GUI for this kind of View or use the "old-fashioned" way. I would not try to hack around the UITabBar as your app may be rejacted by Apple.
I am passing an NSDictionary object from one view class to another as I transition from a table view to a normal view to show details:
Passing Controller:
[tweetController setTweet:tweet];
Receiving Controller.h:
#interface TweetViewController : UIViewController {
NSDictionary *tweet;
}
#property (nonatomic, retain) NSDictionary *tweet;
Receiving Controller.m:
#implementation TweetViewController
#synthesize tweet = _tweet;
I then try to use this information to set the properties of some fields in my view:
- (void)viewDidLoad
{
[super viewDidLoad];
tweetLabel.text = [_tweet objectForKey:#"text"];
}
The result is a blank label and if I inspect the value of _tweet at this stage it is nil.
I originally had a method which set the value of tweet which I called at the same location as I am now setting the value. If I inspected the value at this stage it was fine.
I presume that the automagic setter through #synthasize is working, but somewhere else the value is being lost.
Sorry this is my first objective C anything! Thanks for any help in advance.
You are using your "tweet" instance variable, whereas the "tweet" property is synthesized to the "_tweet" variable.
You are probably calling the setTweet method after viewDidLoad executes.
I usually pass this kind of thing into a custom init method.
Alternatively, you could do the set before pushing the detail VC onto the nav stack.
Are you sure that tweetLabel isn't nil?
I've made a few corrections & optimisations to your code. You don't need to declare ivars in the header file anymore, they are generated automatically by #synthesize
- (void)dealloc; is only needed if you're not using ARC.
//.h
#interface TweetViewController : UIViewController
#property (strong, nonatomic) NSDictionary *tweet;
#property (strong, nonatomic) IBOutlet UILabel *tweetLabel
#end
//.m
#implementation TweetViewController
#synthesize tweet = _tweet;
#synthesize tweetLabel = _tweetLabel;
- (void)viewDidLoad {
[super viewDidLoad];
self.tweetLabel.text = [self.tweet objectForKey:#"text"];
}
- (void)dealloc {
[_tweet release];
[_tweetLabel release];
[super dealloc];
}
#end
Note: strong is equivalent to retain
To expand on #Rayfleck's answer, since you are new to Objective-C, your custom init method could look like this:
In TweetViewController.h:
- (id)initWithTweet:(NSDictionary*)tweet;
In TweetViewController.m:
- (id)initWithTweet:(NSDictionary*)tweet
{
self = [super init];
if (self) {
_tweet = tweet;
}
return self;
}
and then in your passing controller you'd allocate and initialize like this:
TweetViewController *tvc = [[TweetViewController alloc] initWithTweet:myTweet];
Here's the basic code (based on Xcode's Tabbed Applicaion Template)
ViewController.h
#interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic,retain) NSMutableArray *movies;
#property (nonatomic,retain) IBOutlet UITableView *tableView;
ViewController.m
#implementation ViewController
#synthesize movies,tableView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Watchlist", #"Watchlist");
self.tabBarItem.image = [UIImage imageNamed:#"watchlist"];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"tableView = %#", tableView);
}
Output
tableView = (null)
The TableView is connected to File's owner in IB with class is set to ViewController
I really don't get why the tableView is null.
I'm not a complete newbie to Cocoa (but to the iPhone SDK), I created a Single View based Application with a TableView dataSource to see if I was missing something. I got it working in under a minute.
Anybody can help out?
In interface builder right click File's Owner and ensure that the following connections are made:
Outlets
tableView - Table View
Referencing Outlets
dateSource - Table View
delegate - Table View
I suspect you may have not made the first connection?
Make sure the nib/xib is included in your current target.
I just experienced this issue on Xcode5 with iOS7 SDK. Strangely enough, I discovered that the nib wasn't included in my target anymore. I don't know when and why that happened, but it had this strange side effect that most IBOutlets weren't set up properly, even if all connections from code to nib/xib were fine.
For example: my MKMapView *map was in my list of subviews on viewDidLoad:, but the IBOutlet MKMapView *map property in my view controller was still nil. After I ticked the "include in target" checkbox, everything worked as expected.