Storyboard and views transitions - iphone

I'm currently learning iPhone applications development, and I made some online tutorials to learn how all this is working.
I'm now quite used to the Objective-C concepts, and I'm trying to build a first application based on two views :
The first view would be the "Login view", simply with a kind of login system : a login field and a password field, and a "connect" button.
The second view is the "Home view" of the application, which will be called after the login.
I made a push segue to make the relation between the Login view and the view that is called after login. Here's what the storyboard looks like :
What I don't know actually is how to call a function that will check if the credentials are correct, and the switch to the other view if the login succeed.
Can anyone explain me, or give me some tips / tutorials for this please ?
Here are the sources for my LoginController :
LoginController.h
#interface LoginController : UIViewController {
IBOutlet UITextField *TFLogin;
IBOutlet UITextField *TFPassword;
}
#property (strong, nonatomic) IBOutlet UITextField *TFLogin;
#property (strong, nonatomic) IBOutlet UITextField *TFPassword;
- (IBAction)Connect:(UIButton *)sender;
#end
LoginController.m
#implementation LoginController
#synthesize TFLogin;
#synthesize TFPassword;
- (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.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (IBAction)Connect:(UIButton *)sender
{
if ([TFLogin.text isEqualToString:#"myLogin"] && [TFPassword.text isEqualToString:#"myPassword"]) {
[self performSegueWithIdentifier:#"LoginSegue" sender:sender];
NSLog(#"Connection OK");
}
else {
NSLog(#"Connection Not OK");
}
}
#end
Thanks !

You have two choices for triggering a segue. The easy way is just to ctrl-drag in interface builder from the button to the next view controller. You can also do it in code (in an IBAction), by calling performSegueWithIdentifier:sender:.
If you go with the IBAction, you can validate the data there.
If you go with the interface builder method, you can't validate -- prepareForSegue:sender: will be too late. Anyway, there's a possible stumbling block here -- as I recall, UINavigationController doesn't forward prepareForSegue:sender: to its children. You can mitigate this with a category on UINavigationController or by subclassing.

Related

event delegation

I have two uiviewcontroller: MainViewController and SecondaryViewControlle. In MainViewController I do:
[self.view addSubView:SecondaryViewControlle.view];
The SecondaryViewController is a button by pressing the function to be performed by a MainViewController. How to do?
You'd start by defining a protocol in your SecondViewControlle.h file, something like:
#protocol SecondViewControlleDelegate
- (void) doSomething
#end
You would also need to add a "delegate" ivar to your SecondViewControlle .h file. It would be the delegate line:
#interface SecondViewControlle : UIViewController
...
...
...
#property (nonatomic, assign) id delegate; // all you need to do is add this line inside your interface declarations
...
...
...
#end
Then, when you create / instantiate your SecondaryViewControlle from your MainViewController, make certain to add the MainViewController as the delegate like so:
SecondaryViewControlle.delegate = self;
[self.view addSubView:SecondaryViewControlle.view];
Now the "delegate" of your SecondaryViewControlle view controller points back to your MainViewController.
And when the button is pressed, you can simply do something like:
- (IBAction) buttonIsPressed: (id) sender
{
[delegate doSomething];
}
Now, I need to give you some advice here.
1 ) DO NOT use the class names as object names. Instead of having an object named "SecondViewControlle", name it something different (and start it with a lower case, which is Objective-C convention), something like "moreDetailVC".
2) I've told you how to do this with a delegate pattern, but this may not be the most appropriate way to do whatever it is that you're trying to do. After all, the MainViewController object (which should be renamed mainVC to differentiate the object from the class) is not on screen or visible so maybe there's a better place to put the functionality?
Option A
It's quicker, and easier, but lacks the maintainability, since there is no contract stating that SecondaryViewController needs to bother calling anything, and self.parentViewController could be any UIViewController.
Option B
The delegate pattern; this is my preference, it's obvious what's happening, what's required, and there's a nice solid contract that states, if you want to initialise me, give me a delegate.
Option C
If SecondaryViewController has to notify multiple objects, it would be quick to use the NSNotificationCenter, but as with Option A, there's no contract, should you need to notify many objects, you would need to remember to listen for notifications on those objects - since this is not the question, I won't go into detail, it's just here for the information
Option A
Within MainViewController.m, do something like so:
SecondaryViewController *viewcontroller = [[SecondaryViewController alloc] initWithNibName:#"SecondaryView" bundle:nil];
[self addChildViewController:viewcontroller];
//set viewcontroller.view frame
[self.view addSubview:viewcontroller.view];
[viewcontroller didMoveToParentViewController:self];
Inside MainViewController.h
-(void) performButtonClickAction;
Inside MainViewController.m:
-(void) performButtonClickAction {
//Do something constructive
}
and then inside the SecondaryViewController.m:
-(IBAction) buttonPressed:(id) sender {
[self.parentViewController performButtonClickAction];
}
Option B
Inside SecondaryViewController.h
#protocol SecondaryViewControllerDelegate <NSObject>
-(void) eventAFromViewController:(UIViewController *) viewController;
-(void) eventBFromViewController:(UIViewController *) viewController;
#end
#interface SecondaryViewController : UIViewController {
id<SecondaryViewControllerDelegate> delegate;
}
#property (assign, nonatomic) id<SecondaryViewControllerDelegate> delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil delegate:(id<SecondaryViewControllerDelegate>) theDelegate;
#end
Inside SecondaryViewController.m
#synthesize delegate = _delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil delegate:(id<SecondaryViewControllerDelegate>) theDelegate
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.delegate = theDelegate;
}
return self;
}
-(IBAction) buttonPressed:(id) sender {
if( self.delegate != nil ) {
[_delegate eventAFromViewController:self];
}
else {
//No delegate
}
}

IPhone iOs 5, need help getting my tab bar at the top to work

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.

EXEC_BAD_ACCESS in iOS5 for IBAction

I have written a sample code for button click action in xcode 4.2 for iOS5.
Here is the code
.h
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController
#property(strong,nonatomic) IBOutlet UIButton *button;
-(IBAction)changed;
#end
.m
#import "FirstViewController.h"
#implementation FirstViewController
#synthesize button=_button;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
// Custom initialization
}
return self;
}
- (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.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[_button addTarget:self action:#selector(changed) forControlEvents:UIControlEventTouchUpInside];
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
-(IBAction)changed
{
NSLog(#"clicked");
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
#end
But when I click the button. I am getting exception. how to solve this issue? the same is working in iOS 4.3
first change this code into this one
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[_button addTarget:self action:#selector(changed) forControlEvents:UIControlEventTouchUpInside];
}
I have found solution for my problem.
The main problem is that, if I create a view controller object locally (i.e., inside any method) and add it as subview then, when you invoke any IBAction, at that time it will raise an exception because, the memory for that viewController is getting deallocated automatically when declared locally.
I guess he needs to add data member for that button as well. I mean code should look like this.
#interface FirstViewController : UIViewController{
IBOutlet *UIButton *button;
}
#property(strong,nonatomic) IBOutlet UIButton *button;
-(IBAction)changed;
#end
and try replacing
#synthesize button = _button;
with
#synthesize button;
in your .m file.
You have the wrong message signature for your changed action. It should be:
- (IBAction)changed:(id)sender
and in the addTarget line of code:
#selector(changed:)
PS: Why are you using _button? I don't think this is related to the problem, but you should be using self.button instead. Accessing instance variables directly should be avoided, especially in this case, where you are allowing the compiler to decide what name the variable should have.
PPS: As mentioned by #InderKumarRathore, you should also be calling [super viewDidLoad] before running your own code.

IBOutlet UITableView is null after View did load

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.

Connecting To a Label Within A UIView Of A Tab

I am creating my first tab controller app. I have 2 tabs with 2 UIViews in them. I did this mostly from Interface Builder all I did in Xcode was add 2 files firstControllerView and SecController view. I can see the tab controller is working went I run the app (I simply changed the background color on the 2 UIViews in the tabs to see the effect).
Now I want to add a label to the secondView and set its text programmatically from code. This is whats breaking for me! I am doing something wrong. In my SecondViewController.h it looks like this:
#interface SecondViewController : UIViewController {
IBOutlet UILabel *title;
}
#property (nonatomic,retain) UILabel *title;
#end
and the .m looks like this...
#import "SecondViewController.h"
#implementation SecondViewController
#synthesize title;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[title setText:#"Hello Nick"];
[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 {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[title release];
[super dealloc];
}
#end
After this I went back to Interface Builder and dragged the outlet reference to the label. When I run the simulator it crashes.
What am I missing here? It must be something simple.
Forgot to create an outlet for a tabbarcontroller in the app delegate then connect that outlet to the tabbar controller in interface builder.