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.
Related
I know this question has been asked, but I have read the responses, adopted the fixes and still have an issue somewhere which I can't identify.
I have an IOS app which is similar in nature to email. It has an InboxVC which is a tableVC with a custom prototype cell which upon selection triggers a messageDetailVC.
The issue is that the messageDetailVC is triggered but the values are not being passed to it. I added log messages to evaluate the value in code before getting to Storyboard issues, and the variable being passed (messageID) has a NULL value.
Can someone tell me what I am missing or doing wrong? My code is:
InboxVC.m (snippet)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSLog(#"%#: Message touched...", LOG_TAG);
messageDetail *mdvc = [[messageDetail alloc] init];
mdvc.messageID = #"123456789-1";
[self.navigationController pushViewController:mdvc animated:YES];
NSLog(#"%#: messageID value is = %#",LOG_TAG,mdvc.messageID);
//messageID has valid value here
}
messageDetail.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface messageDetail : UIViewController
{
NSString *messageID;
}
#property (strong, nonatomic) NSString *messageID;
#property (strong, nonatomic) IBOutlet UITextView *body;
messageDetail.m
#import <Foundation/Foundation.h>
#import "messageDetail.h"
static NSString * LOG_TAG = #"messageDetailController";
#implementation messageDetail
#synthesize messageID;
#synthesize body;
- (void)viewDidLoad {
[super viewDidLoad];
body.text = messageID;
//debug messages
NSLog(#"%#:Added messageID as Body text", LOG_TAG);
NSLog(#"%#:Value of body.text is = %#", LOG_TAG, body.text);
NSLog(#"%#:Value of messageID is = %#", LOG_TAG, messageID);
//messageID has null value here
}
- (void) viewWillAppear:(BOOL)animated {
[self.navigationController setNavigationBarHidden:NO];
}
MainStoryboard:
I have a messageDetail view tied to the messageDetail sub class of UIViewController. There is a segue with identifier "s_msgDetail" from the prototype cell's selection event to the messageDetailVC (show). As I said above the messageDetailVC appears, just not with the body.text being set to the value of "messageID".
You are setting the messageID on the wrong instance of your messageDetail view controller. Since you are using storyboards, the storyboard is already creating and presenting an instance of messageDetail for you. But, in tableView:didSelectRowAtIndexPath: you are creating a second one and presenting it. (I'm surprised you're not getting a bunch of runtime animation warnings when doing that.)
You should be setting messageID in prepareForSegue:sender: like this:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"s_msgDetail"]) {
messageDetail * vc = (messageDetail *) segue.destinationViewController;
NSIndexPath * indexPath = [self.tableView indexPathForSelectedRow];
vc.messageID = /* use indexPath to get data from messages collection */;
}
}
You have to use prepareForSegue to pass value to another view Controller
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 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 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.
I have an object that is sent from my main VC to my MasterTabViewController(UITabBarController) in the viewDidLoad I NSLog the object and it shows the object, good. Now I need that object to go to the first tab UIViewController. I tried multiple times and cannot get it to go. I am new so forgive my ignorance,
I send the object from my main vc to my MasterTabViewController via segue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showLogin"])
{
MasterTabViewController *preview = segue.destinationViewController;
preview.communityTapped = self.tempCommunity;
}
}
^This works fine!^ self.tempCommunity is an instance community object.
MasterTabViewController.h
- (void)viewDidLoad
{
[super viewDidLoad];
FirstTabViewController *firstvc;
firstvc.communityTapped = self.communityTapped;
NSLog(#"%# !!!!!!!!! ",self.communityTapped.commDescription);
// Do any additional setup after loading the view.
}
FirstTabViewController.h
#property (nonatomic, strong) IBOutlet UILabel *descriptionLabel;
#property (nonatomic, strong) Community *communityTapped;
FirstTabViewController.m
- (void)viewDidLoad
{
self.descriptionLabel.text = self.communityTapped.commDescription;
[super viewDidLoad];
// Do any additional setup after loading the view.
}
If anyone could help that would be greatly appreciated as I have tried and failed many times.
You can't set up IBOutlets to a tab bar controllers view controllers (and I see from your project that you never hooked them up). In your viewDidLoad for the tab bar controller, you can get a reference to any of its view controllers with the viewControllers property. So do something like this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.firstvc = self.viewControllers[0];
self.firstvc.communityTapped = self.communityTapped;
NSLog(#"%# !!!!!!!!! ",self.communityTapped.commDescription);
}
I think the problem is in the viewDidLoad of your MasterTabViewController.h. You're creating the variable firstvc, but you're not setting the value to anything. So when you set the value of communityTapped in the next line, you're trying to set the value of communityTapped on nothing.
Option 1
If you've set up the tab in interface builder, you need to create an IBOutlet in your MasterTabViewController.h and connect it to your view controller. Something like the following:
#property (strong, nonatomic) IBOutlet FirstTabViewController *firstvc;
Then to set the value of the communityTapped, property, you would use something like this:
self.firstvc.communityTapped = self.communityTapped
Option 2
The other option would be to create the tab pragmatically, and add it to the view. I'm not sure quite what your setup is, but I imagine it would be something like this:
FirstTabViewController *firstvc = [[FirstTabViewController alloc] init];
firstvc.communityTapped = self.communityTapped;
NSArray *viewControllers = [[NSArray alloc] initWithObjects:firstvc, nil];
[self.navigationController.tabBarController setViewControllers:viewControllers animated:NO];