I have followed the "Start Developing iOS Apps Today" tutorial.
The application has generally built as I expected. However the following block has not worked.
o link buttons to the unwindToList: action
1) In the project navigator, select Main.storyboard.
2) On the canvas, Control-drag from the Cancel button to the Exit item in the add-to-do-item scene dock.If you don’t see the Exit item in the scene dock but instead see the description of the scene, click the Zoom In image: ../Art/zoom_in_2x.png button on the canvas until you see it.A menu appears in the location where the drag ended.
3) Choose unwindToList: from the shortcut menu.This is the action you just added to the XYZToDoListViewController.m file. This means that when the Cancel button is tapped, the segue will unwind and this method will be called.
4) On the canvas, Control-drag from the Done button to the Exit item in theXYZAddToDoItemViewController scene dock.
5)Choose unwindToList: from the shortcut menu.
All the other directions on the tutorials have worked. The build compiles without error. When the app runs in the Simulator the user clicks Done or Cancel, but the focus does not move back to the ToDoList scene. It just stays on the Add Item screen.
Any ideas on what is happening.
iOS 7.1
Some further detail
This is the ToDoController.m with the unwindToList
-(IBAction)unwindToList:(UIStoryboardSegue *)segue
{
ianNo00004AddToDoItemViewController *source = [segue sourceViewController];
ianNo00004ToDoItem *item = source.toDoItem;
if (item != nil) {
[self.toDoItems addObject:item];
[self.tableView reloadData];
}
}
**This is the ToDoListController.h**
#interface ianNo00004ToDoListViewController : UITableViewController
-(IBAction)unwindToList:(UIStoryboardSegue *)segue;
#end
The AddToItemViewController.m code
#import "ianNo00004AddToDoItemViewController.h"
#interface ianNo00004AddToDoItemViewController ()
#property (weak, nonatomic) IBOutlet UITextField *textField;
#property (weak, nonatomic) IBOutlet UIBarButtonItem *doneButton;
#end
#implementation ianNo00004AddToDoItemViewController
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if (sender != self.doneButton) return;
if (self.textField.text.length > 0)
{
self.toDoItem = [[ianNo00004ToDoItem alloc] init];
self.toDoItem.itemName = self.textField.text;
self.toDoItem.completed = NO;
}
}
You should add method unwindToList in XYZToDoListTableViewController.h
(not the ItemViewController, but the TableViewController)
#interface XYZToDoListTableViewController : UITableViewController
-(IBAction)unwindToList:(UIStoryboardSegue *)segue;
#end
Then add the following code in XYZToDoListTableViewController.m
- (IBAction)unwindToList:(UIStoryboardSegue *)segue
{
}
The tutorial which you used may be the old version, there might be some errors.
Follow the latest version here:
https://developer.apple.com/library/ios/referencelibrary/GettingStarted/RoadMapiOS/SecondTutorial.html#//apple_ref/doc/uid/TP40011343-CH8-SW7
I was doing the same example and had mine working. Have a look at the connections inspector on your Add To-Do Item on the storyboard for both the cancel and done buttons and see if they have actions for the triggered Segues.
I removed the action from both buttons on my project and the Add To-Do Item wouldn't close, so I think you are missing those actions.
Related
So ultra NOOB when it comes to iPhone development
I have three view controllers for a Tab Bar Controller.
What I want here is when I press a button the data from TextFields in the first view get fetched and displayed.
In my firstViewController.h I have declared the TextFields like this:
IBOutlet UITextField * driver;
IBOutlet UITextField * phone;
IBOutlet UITextField * poe;
IBOutlet UITextField * doe;
IBOutlet UITextField * coe;
IBOutlet UITextField * dod;
IBOutlet UITextField * customer;
IBOutlet UITextField * customerContact;
IBOutlet UITextField * customerTank;
IBOutlet UITextField * trailer;
IBOutlet UITextField * truck;
Then in my Main.storyboard I have connected the outlets to each TextField
Now I thought that it just was to import the firstViewController.h to my thirdViewController.h using the #import "" and somehow check when button click was trigged. But it does not seems to be that easy in terms of NOOBines.
I thought that doing something like this in the thirdViewController.h could make the magic:
#import <UIKit/UIKit.h>
#import "FirstViewController.h"
#interface ThirdViewController : UIViewController{
NSString *theDriver = [driver.text];
}
#end
unfortunately it isnt that easy. assuming there is a secondViewController in between your first and third, you're going to pass variables through during the segue using this method. define some variables in the .h file of your second view controller like driver, phone, etc. and assign those values from what you have right there
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"the name of your segue"]) {
secondViewController *secondVC = segue.destinationViewController;
secondVC.driver = self.driver.text;
secondVC.phone = self.phone.text;
//continue typing your values and use this to carry this same method in your secondViewController to carry them into your thirdViewController
}
EDIT 1:
Here is an example I used in an app of mine where I am looking at a list of games and I want to look at one individual game. This is the code in the MatchesViewController.m file. I have my gameObject (in your case the gameObject would be text) and I want to send it to the next view controller. I have also have this imported in the "MatchesViewController.m"
#import "EachMatchViewController.h"
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showOneGame"]) {
EachMatchViewController *eachVC = segue.destinationViewController;
eachVC.gameObject = self.gameObject;
}
}
This is what is defined in my "EachMatchViewController.h" which is the view controller I am going to.
#property (strong, nonatomic) PFObject *gameObject;
some in my "EachMatchViewController.m" I can use it the data that is being passed over.
Can I change a segue identifier depending on what another segue identifier is in code? I'm not even sure if that is the correct question for what I am looking to do.
I have a ViewController which has a number of buttons on it. Each button has a segue to another ViewController which contains some labels, images and buttons. The text/images/titles of these labels, images and buttons are determined by which segue (which button on the previous ViewController) is pressed. This is done by giving each property a variable in the initial ViewController and assigning that variable in the 2nd ViewController.
eg. (I will not individually put the .h and .m but just under one heading!)
1st ViewController:
-(void) prepareForSegue: (UIStoryboardSegue*)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"someSegIdentifier"]){
2ndViewController *aVC = (2ndViewController *)(segue.destinationViewController);
aVC.TitleString = #"A Title";
aVC.FImg = [UIImage imageNamed:#"someimage.jpg";
aVC.Button1String = #"Button 1 Text";
aVC.Button2String = #"Button 2 Text";
etc...
} }
2nd View Controller:
#property (strong, nonatomic) UIImage *FImg;
#property (strong, nonatomic) NSString *TitleString;
#property (strong, nonatomic) NSString *Button1String;
#property (strong, nonatomic) NSString *Button2String;
#property (strong, nonatomic) IBOutlet UILabel *TitleLabel;
#property (strong, nonatomic) IBOutlet UIImageView *FImageView;
#property (strong, nonatomic) IBOutlet UIButton *Button1;
#property (strong, nonatomic) IBOutlet UIButton *Button2;
-(void)viewDidLoad
{
self.TitleLabel.text = self.TitleString;
self.FImageView.image = self.FImg;
[self.Button1 setTitle:self.Button1String forState:UIControlStateNormal];
[self.Button2 setTitle:self.Button2String forState:UIControlStateNormal];
}
From this ViewController, each button links to another ViewController each (which have NavControllers Embeded), these 2 ViewControllers also have 2 buttons which individually link to a tableViewController. The data displayed in the tableView is determined by the segue ID from the previous 2 viewControllers.
I believe I need to repeat the first process (the code above) on the second set of viewControllers but at the same time change the segue ID for proceeding/destination viewController, so that the tableView can load the correct data. How can I change this Identifier?
I think a hierarchy may be and easier way of explaining it.
How would I go about changing the specific segue Identifiers depending on what the previous ViewController shows. Keeping in mind, that for each level their are many different 'links' or specified properties.
Yes you are definitely asking the wrong question. You are creating a segue for each button?
That is the wrong approach as segue are meant to be between 2 view controllers.
if you had 100 button would you create 100 segues?
In your case you have 2 controllers so it should be only one segue.
If you need to know which button was pressed I suggest you to give the sender a tag number and check it when preparing to segue.
You could, instead of sending the information from previous controllers through the segue identifier, create properties on the destination view controllers and change those properties at the time of segue TO that view controller according to the button pressed ( or segue identifier). Then, when there is a segue FROM the view controller, you use those properties and the segue identifier as parameters, instead of just the segue identifier.
e.g.:
if you go are going from controller A to B to C, and there are 3 possible segues from A to B and 3 possible segues from B to C, you could have a NSString property in B which would go like
in A:
-(void) prepareForSegue: (UIStoryboardSegue*)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"identifier1"]){
2ndViewController *aVC = (2ndViewController *)(segue.destinationViewController);
aVC.propertyIWasTalkingAbout = "segue1"
}
else if ([segue.identifier isEqualToString:#"identifier2"]){
2ndViewController *aVC = (2ndViewController *)(segue.destinationViewController);
aVC.propertyIWasTalkingAbout = "segue2"
}
else if ([segue.identifier isEqualToString:#"identifier3"]){
2ndViewController *aVC = (2ndViewController *)(segue.destinationViewController);
aVC.propertyIWasTalkingAbout = "segue3"
}
}
in B:
-(void) prepareForSegue: (UIStoryboardSegue*)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"identifier4"]){
switch (self.propertyIWasTalkingAbout){
case("segue1"):
//...
break;
case("segue2"):
//...
break;
case("segue3"):
//...
break;
}
}
else if ([segue.identifier isEqualToString:#"identifier5"]){
switch (self.propertyIWasTalkingAbout){
case("segue1"):
//...
break;
case("segue2"):
//...
break;
case("segue3"):
//...
break;
}
}
else if ([segue.identifier isEqualToString:#"identifier6"]){
switch (self.propertyIWasTalkingAbout){
case("segue1"):
//...
break;
case("segue2"):
//...
break;
case("segue3"):
//...
break;
}
}
}
I wrote all this code here, not on XCode, so there might be some errors, but I hope you get the idea, and I also hope I got what you meant :p
I am new to iOS XCode and am attempting to complete the initial example. Everything works as expected and in debugging with breakpoints I cannot seem to set a value from the Add To-Do Item to show up in Self.textField, so the program just skips each of these steps and I am back to the initial list. UPDATE: It definitely does not seem to pass this line of code,
if (sender != self.doneButton) return;
thus all the other code does not execute either. Is it something to do with the configuration of the Text Field in the story board? - Thanks!
In AddToDoItemViewController.m the value in the text field does not seem to come across and get stored in this code.
#import "AddToDoItemViewController.h"
#interface AddToDoItemViewController ()
#property (weak, nonatomic) IBOutlet UITextField *textField;
#property (weak, nonatomic) IBOutlet UITextField *doneButton;
#end
#implementation AddToDoItemViewController
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if (sender != self.doneButton) return;
if (self.textField.text.length > 0) {
self.toDoItem = [[ToDoItem alloc] init];
self.toDoItem.itemName = self.textField.text;
self.toDoItem.completed = NO;
}
}
In ToDoListTableViewController.m, the action just sees that item is NILL so it is done...
- (IBAction)unwindToList:(UIStoryboardSegue *)segue
{
AddToDoItemViewController *source = [segue sourceViewController];
ToDoItem *item = source.toDoItem;
if (item !=nil) {
[self.toDoItems addObject:item];
[self.tableView reloadData];
}
}
It's driving me crazy as I have everything else working fine and am learning how nice the debugger is, but I just can't see where the problem is. Do you know if you can download the final code sample somewhere?
Thanks for any help here. It's July 4th and I won't be sleeping for quite a while... :}
I have checked the document. You did something wrong. In the example the doneButton is a UIBarButtonItem.
Did you also do this:
To create the unwind segue, link the Cancel and Done buttons to the unwindToList: action through the Exit icon in the dock of the source view controller, XYZAddToDoItemViewController.
I have a UITabBarController, which has 3 UIViewControllers. Each controller has a few UITextFields. In UItabBarController is a button "save". Please, how can I get data from UITextFields in UIViewControllores? I need the data from all UITextFields and from all UIViewControllers in UITabBarController (in IBAction of save button). How can I do it? Thanks.
Here is picture of my TabBarController on Storyboard.
The first ViewController has a class StudentVC. The class has one text field. In StudentVC.m is this code:
#import <UIKit/UIKit.h>
#import "CoreDataHelper.h"
#interface StudentVC : UIViewController <UITextFieldDelegate>
#property (strong, nonatomic) NSManagedObjectID *selectedStudentID;
#property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
#property (strong, nonatomic) IBOutlet UITextField *studentNameTextField;
#end
The studentNameTextField is connected with field in Storyboard. Then I have a class for TabBar the class name is StudentTBC. In file StudentTBC.m I have this code:
- (void)viewDidLoad
{
if (debug == 1) {
NSLog(#"Running %# '%#'", self.class, NSStringFromSelector(_cmd));
}
[super viewDidLoad];
StudentVC *studentVC = [self.tabBarController.viewControllers objectAtIndex:0];
studentVC.studentNameTextField.text = #"asad";
}
So, when I go on TabBar I will see in studentNameTextField the text "asad", but the field is clear... So I added also in IBAction:save this code:
- (IBAction)save:(id)sender {
if (debug == 1) {
NSLog(#"Running %# '%#'", self.class, NSStringFromSelector(_cmd));
}
StudentVC *studentVC = [self.tabBarController.viewControllers objectAtIndex:0];
NSString *string = studentVC.studentNameTextField.text;
NSLog(#"string:%#", string);
But when I insert the field and press "ulozit" (its save in czech), I get a "string: null" instead of a "string: textWhichIWriteToField". I tried indexes 0,1,2 but nothing works. :/
Edited 3.4 15:20
I did a move of my application. In first part is my storyboard - UITabBarController and 3UIViewControllers. I deleted a Navagition Controllers. In second part I do again a Outlets. Third part is code of save function (the code could be write a content of textfield) and code of ViewDidLoad of TabBarController (code could be set a string of textfield). The last part is run application (the result is in console - null string and clear textfield).
I also tried print this:
NSInteger countOfViews = [self.tabBarController.viewControllers count];
NSLog(#"count:%ld", (long)countOfViews);
It has to be three? or? Because my result was 0.. Why? :/
My movie: https://www.youtube.com/watch?v=j6p__e48lLI&feature=youtu.be
EDITED 2.4 20:58 -------------------
Hello. I tried all options but nothing worked.. So I did a new SIMPLE application, where is only one UITabBarController with two UIViewControllers and one UITableViewController (because of navigation bar). Please download the project and try it yourself. Only run, then click on button "TabBar" and you are on the UITabBarController. When you press a Save button, it should display a count of UIViewsControllers and the value of textfields. Could you please try and tell me what's wrong? Thank you. Here is the project:
https://www.dropbox.com/s/r4jlzadr2us00ad/TBC.zip
To access to one of text field you can use code below:
- (IBAction)save {
YourUIViewController *vc = [self.tabBarController.viewControllers objectAtIndex:indexOfController];
NSString *str = vc.yourTextField.text;
}
EDIT. You have me how your controllers are connected and now i realized you got UINavigaitonController after UIViewController. Modified code should look like:
- (IBAction)save {
NSArray *navConArr = [self.tabBarController.viewControllers objectAtIndex:indexOfController];
for(UINavigationController *nc in nacConArr) {
UIViewController *vc = [nc.viewControllers objectAtIndex:0];
NSLog(#"And your text is = %#", vc.studentNameTextField.text);
}
}
Updated
I solved the problem finally... The right code for TabBarController
- (IBAction)save {
YourUIViewController *vc = [self.viewControllers objectAtIndex:indexOfController];
NSString *str = vc.yourTextField.text;
}
Please check outlets for UITextFields,
Also check output for
NSLog(#"StudentVC textField -- %#",studentVC.studentNameTextField); in save()
if it is also null, then there is definitely problem with your outlets.
I have updated my Xcode and suddenly all Buttons, which are connected in the storyboard to the selectors, doesn't work anymore. All programmatically coded Buttons and gesture recognizers work. Partly, they are calling the same IBActions.
What have I done...
Just add a Button and a Label to the View in the Storyboard. In the View Controller .h I added a Outlet to the Label and declare the IBAction.
The files:
.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *myLabel;
-(IBAction)button_pressed:(id)sender;
-(IBAction)swipe_active:(id)sender;
#end
.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize myLabel;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UISwipeGestureRecognizer *swipe_left = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipe_active:)];
swipe_left.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipe_left];
}
-(IBAction)button_pressed:(id)sender{
myLabel.text=#"Button is Running";
}
-(IBAction)swipe_active:(id)sender{
myLabel.text=#"Swipe is Running";
}
#end
And just the Button does not work.
StoryBoard: https://i.stack.imgur.com/1hruM.png
I had this same exact problem after upgrading to xcode 4.5. The IBActions appear as though they are set up correctly in IB but if you look at the .h and .m files, you'll see they're not. If you go to your storyboard and click on your view controller, look under the outlets and you'll see a tab for "Received Actions". Hook up your controls to your actions there and they'll work again.
I have been seeing the exact same issue for many of our apps. In all of my cases it was fixed by reconnecting the action in Interface Builder.