I have a VC of custom type LVSBBSettingsViewController for user settings. The VC is presented by a main menu in LVSMainViewController. The main VC sets the values of the controls in the settings VC programatically. However, when the settings view appears, the controls all revert to the values assigned to them in the storyboard.
I am using delegation to close the settings view and to pass data from the settings VC back to the main VC when it closes. But I don't think that's what's causing the problem since the same thing happens even if I remove that.
What's causing this? I have a feeling I'm missing something really simple here...
LVSBBSettingsViewController.h:
#import <UIKit/UIKit.h>
#class LVSBBSettingsViewController;
#pragma mark LVSBBSettingsViewController Delegate
#protocol LVSBBSettingsViewControllerDelegate <NSObject>
- (void)settingsViewControllerDidCancel:(LVSBBSettingsViewController *)controller;
- (void)settingsViewControllerDidSave:(LVSBBSettingsViewController *)controller;
#end
#pragma mark LVSBBSettingsViewController
#interface LVSBBSettingsViewController : UITableViewController
#property (nonatomic, weak) id <LVSBBSettingsViewControllerDelegate> delegate;
#property (weak, nonatomic) IBOutlet UISwitch *showBranchVarLabelsSwitch;
#property (weak, nonatomic) IBOutlet UISwitch *useAnimationSwitch;
#property (weak, nonatomic) IBOutlet UISwitch *showAllNodesSwitch;
#property (weak, nonatomic) IBOutlet UILabel *tempLabel;
- (IBAction)cancel:(id)sender;
- (IBAction)done:(id)sender;
#end
LVSBBSettingsViewController.m:
#import "LVSBBSettingsViewController.h"
#interface LVSBBSettingsViewController ()
#end
#implementation LVSBBSettingsViewController
// ... Xcode-generated stuff ...
- (IBAction)cancel:(id)sender
{
[self.delegate settingsViewControllerDidCancel:self];
}
- (IBAction)done:(id)sender
{
[self.delegate settingsViewControllerDidSave:self];
}
#end
LVSBBMainViewController.h:
#import <UIKit/UIKit.h>
#import "LVSBBSettingsViewController.h"
#interface LVSMainViewController : UIViewController <LVSBBSettingsViewControllerDelegate>
#end
LVSBBMainViewController.m:
#import "LVSMainViewController.h"
#import "LVSBBMasterViewController.h"
#interface LVSMainViewController ()
#end
#implementation LVSMainViewController
{
LVSBBMasterViewController *bbmvc;
}
// ...
- (void)viewDidLoad
{
[super viewDidLoad];
// Get main storyboard
UIStoryboard *st = [UIStoryboard storyboardWithName:[[NSBundle mainBundle].infoDictionary objectForKey:#"UIMainStoryboardFile"] bundle:[NSBundle mainBundle]];
// Instantiate bbmvc
bbmvc = [st instantiateViewControllerWithIdentifier:#"BBMasterViewControllerStoryboard"];
// Initialize settings
bbmvc.showBranchVarLabels = YES;
bbmvc.useAnimation = YES;
bbmvc.showAllNodes = NO;
}
...
#pragma mark LVSBBSettingsViewController Delegate
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowSettings"])
{
// Get pointer to settings VC
UINavigationController *navigationController = segue.destinationViewController;
LVSBBSettingsViewController *settingsViewController = [navigationController viewControllers][0];
// Set delegate
settingsViewController.delegate = self;
// Populate settings VC
// (same problem occurs if I replace right-hand sides of next 3 lines with NO;)
settingsViewController.showBranchVarLabelsSwitch.on = bbmvc.showBranchVarLabels;
settingsViewController.useAnimationSwitch.on = bbmvc.useAnimation;
settingsViewController.showAllNodesSwitch.on = bbmvc.showAllNodes;
settingsViewController.tempLabel.text = #"HELLO";
}
}
- (void)settingsViewControllerDidCancel:(LVSBBSettingsViewController *)controller
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)settingsViewControllerDidSave:(LVSBBSettingsViewController *)controller
{
// Set settings in bbmvc
bbmvc.showBranchVarLabels = controller.showBranchVarLabelsSwitch.on;
bbmvc.useAnimation = controller.useAnimationSwitch.on;
bbmvc.showAllNodes = controller.showAllNodesSwitch.on;
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
UPDATE: As a workaround, I added properties in LVSBBSettingsViewController that match the properties in LVSMainViewController. In prepareForSegue:sender:, I set those properties instead of setting the controls directly. Then in viewDidLoad in LVSBBSettingsViewController, I set the control values based on the properties. This seems to work. Still not sure why I can't set the control values directly, though.
Related
I am trying yo pass data of two textfields in secondViewController to ViewController and set text of labels in ViewController.
But the delegate method for passing data is not being called. I have checked it by putting break point. Hence label text is not changing.
SecondViewController.h
#import <UIKit/UIKit.h>
#class SecondViewController;
#protocol SecondViewDelegate <NSObject>
-(void)getText1:(NSString*)str1 andText2:(NSString*)str2;
#end
#interface SecondViewController : UIViewController<UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITextField *textField1;
#property (weak, nonatomic) IBOutlet UITextField *textField2;
#property (weak) id<SecondViewDelegate>delegate;
#end
SecondViewController.m
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
#synthesize delegate=_delegate;
- (void)viewDidLoad {
[super viewDidLoad];
self.textField1.delegate=self;
self.textField2.delegate=self;
[self.textField1 becomeFirstResponder];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
if ( textField == self.textField1 ) { [self.textField1 resignFirstResponder]; [self.textField2 becomeFirstResponder]; }
else if ( textField == self.textField2) {
[_delegate getText1:self.textField1.text andText2:self.textField2.text];
NSLog(#"%#",self.textField1.text);
[self.navigationController popToRootViewControllerAnimated:YES];
}
return YES;
}
#end
View Controller.h
#import <UIKit/UIKit.h>
#import "SecondViewController.h"
#interface ViewController : UIViewController<SecondViewDelegate>
-(void)getText1:(NSString *)str1 andText2:(NSString *)str2;
#end
View Controller.m
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *label1;
#property (weak, nonatomic) IBOutlet UILabel *label2;
#end
#implementation ViewController
#synthesize label1;
#synthesize label2;
- (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.
}
- (IBAction)onClick:(id)sender {
SecondViewController* sv= [[SecondViewController alloc] init];
sv.delegate=self;
[self performSegueWithIdentifier:#"moveToSecondController" sender:self];
}
-(void)getText1:(NSString *)str1 andText2:(NSString *)str2{
[label1 setText:str1];
[label2 setText:str2];
}
#end
The problem is that you've created two SecondViewController objects and made your ViewController the delegate of the wrong one.
This: [[SecondViewController alloc] init] creates an object in code. This: [self performSegueWithIdentifier:#"moveToSecondController" sender:self] creates an object from a storyboard definition.
Don't bother creating the first one, just perform the segue. Then, implement the prepareForSegue method and set your delegate there, using the destination controller (which will be the correct SecondViewController).
Try setting your delegate in prepareForSegue method like:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"YOUR_SEGUE_NAME_HERE"])
{
// Get reference to the destination view controller
SecondViewController *vc = [segue destinationViewController];
vc.delegate=self;
}
}
You don't need to declare your delegate method in ViewController.h. It has already been done in SecondViewController.h as the delegate method.
import
import "SecondViewController.h"
#interface ViewController : UIViewController
// Remove this method in ViewController.h file this is call as a simple method for ViewController class
-(void)getText1:(NSString *)str1 andText2:(NSString *)str2;
#end
I have two storyboards and each one has its own respective view controller but I need to change the appearance of the second storyboard based on the button pressed in the first view controller.
In the first view controller I have:
// First view controller .h
#import <UIKit/UIKit.h>
#import "SecondViewController.h"
#interface FirstViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIButton *LevelOneButton; // tag 0
#property (strong, nonatomic) IBOutlet UIButton *LevelTwoButton; // tag 1
-(IBAction)selectLevel:(UIButton *)sender; // both buttons connected to this method
#property (assign, nonatomic) int levelSelect;
#end
then in the first .m file:
//FirstViewController.m
-(IBAction)selectLevel:(UIButton *)sender {
if (sender.tag == 0) {
_levelSelect = 0;
}
if (sender.tag == 1) {
_levelSelect = 1;
}
}
This code works fine but the problem occurs in the secondViewController that I have. When I try and access the levelSelect property in the SecondViewController I get the errors "Property 'levelSelect' not found on object of type 'FirstViewController'" or "Unexpected identifier levelSelect" or something among those lines. I've tried every single thing I could think of and every question I found on StackOverflow relating to this but none have fixed the problem. Anyone know what I'm doing wrong?
You should be setting the property on the second view controller as you're pushing or segueing.
So in your first view controller it should look something like this:
#import "ViewController.h"
#import "SecondViewController.h"
#interface ViewController ()
#property (strong, nonatomic) IBOutlet UIButton *levelOne;
#property (strong, nonatomic) IBOutlet UIButton *levelTwo;
#property (assign, nonatomic) int selectedLevel;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.levelOne.tag = 1;
self.levelTwo.tag = 2;
}
- (IBAction)selectLevel:(UIButton *)sender
{
if (sender.tag == 1) {
self.selectedLevel = 1;
} else {
self.selectedLevel = 2;
}
[self performSegueWithIdentifier:#"pushToSecond" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
SecondViewController *dest = segue.destinationViewController;
dest.levelSelect = self.selectedLevel;
}
#end
Now, when viewDidLoad gets called on the SecondViewController that property will be set and you can use it. Like so:
#import "SecondViewController.h"
#interface SecondViewController ()
#property (strong, nonatomic) IBOutlet UILabel *levelLabel;
#end
#implementation SecondViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.levelLabel.text = [#(self.levelSelect) stringValue];
}
#end
Quick Edit, if you're not using segues you can do the same thing by pushing manually. Would look something like:
- (IBAction)selectLevel:(UIButton *)sender
{
if (sender.tag == 1) {
self.selectedLevel = 1;
} else {
self.selectedLevel = 2;
}
SecondViewController *secondVC = [[UIStoryboard storyboardWithName:#"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:#"second"];
secondVC.levelSelect = self.selectedLevel;
[self.navigationController pushViewController:secondVC animated:YES];
}
I have a big noob problem. I new in iOS and I'm trying to do this:
I have two ViewControllers. The first one has an button that if it is pushed, control goes to a second view controller.
That works but the problem is when I try to get data in my second view controller. The UITextfield doesn't work.
I'm trying to display which I insert into textfield in the label. But THE TEXTFIELD DOESN'T WORK :(
I putted the IBOutlets succesfully in the xibs and connect the buttons with their IBActions...
This is my code:
ViewController.h
#import <UIKit/UIKit.h>
#import "SecondViewController.h"
#class SecondViewController;
#interface ViewController : UIViewController
#property (strong, nonatomic) SecondViewController *secondViewController;
-(IBAction)buttonClicked:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#implementation ViewController
#synthesize secondViewController;
-(IBAction)buttonClicked:(id)sender {
self.secondViewController = [[SecondViewController alloc]initWithNibName:#"SecondViewController" bundle:nil];
[self presentViewController:self.secondViewController animated:YES completion:nil];
}
SecondViewController.h
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController {
IBOutlet UITextField *textField;
UIButton *obtener;
IBOutlet UILabel *label;
}
#property (nonatomic, retain) IBOutlet UITextField *textField;
#property (nonatomic, retain) IBOutlet UIButton *obtener;
#property (nonatomic, retain) IBOutlet UILabel *label;
-(IBAction)obtenerClicked:(id)sender;
#end
SecondViewController.m
#import "SecondViewController.h"
#implementation SecondViewController
#synthesize obtener, textField, label;
-(IBAction)obtenerClicked:(id)sender {
label.text = textField.text;
}
- (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
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (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
In your SecondViewController.h, add this property:
#property(nonatomic, readwrite) NSString* myString;
now in firstViewController's onClick method, add this line
-(IBAction)buttonClicked:(id)sender {
//....... Previous code....
self.secondViewController.myString= #"String to be sent";
}
This will pass the string for you..
if you want to pass the string in text field, use this:
-(IBAction)buttonClicked:(id)sender {
//....... Previous code....
self.secondViewController.textField.text= #"String to be sent";
}
hope it will work fine for your requirement.
UPDATE:
Do the following to get expected results:
1. make your secondViewController a textView delegate.
in your secondViewController.h
replace #interface SecondViewController : UIViewController with #interface SecondViewController : UIViewController<UITextFieldDelegate>
put a button in your secondViewController & create an IBAction for it.
on Click event of button, write this:
self.label.text= self.textfield.text;
tell me if it does not work.
In UINavigationController this is child controller
.h
#protocol childProtocol <NSObject>
-(void)childMethod:(NSArray*)params;
#end
#property (strong, nonatomic) id<childProtocol>childDelegate;
#property (weak, nonatomic) parentVC *pVC;
.m
if([self.childDelegate respondsToSelector:#selector(childMethod:)]) {
[self.childDelegate performSelector:#selector(childMethod:) withObject:self.arry];
}
This is my parent controller
.m
-(void)childMethod:(NSArray *)params {
// some work
}
...
childVC *cVC = [[childVC alloc]init];
cVC.pVC = self;
But childMethod: is not getting called so I searched on internet and got this post
UINavigationControllers: How to pass value to higher (parent?) controller in stack?
I tried to create a weak reference but dont know how to use to make delegate pass data from child to parent?
Try this. Check the sample project attached
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController
- (void)passData:(NSString *)strText;
#end
ParentViewController.m
- (IBAction)btnGoToSecondView:(id)sender {
ChildViewController *secondVC = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
secondVC.delegate = self;
[self presentViewController:secondVC animated:YES completion:nil];
}
- (void)passData:(NSString *)strText {
NSLog(#"Data Passed = %#",strText);
}
ChildViewController.h
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#class ParentViewController;
#interface ChildViewController : UIViewController
#property(nonatomic, assign) ParentViewController *delegate;
#end
ChildViewController.m
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[self.delegate passData:#"Hello"];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Sample Project
This is child controller.h
#protocol childProtocol <NSObject>
-(void)childMethod:(NSArray*)params;
#end
#property (strong, nonatomic) id<childProtocol>childDelegate;
#property (weak, nonatomic) parentVC *pVC;
.m
if([self.childDelegate respondsToSelector:#selector(childMethod:)]) {
[self.childDelegate performSelector:#selector(childMethod:) withObject:self.arry];
}
This is my parent controller.h
#import <UIKit/UIKit.h>
#import "ChildController.h"
#interface perentController : UIViewController < childProtocol >
.m
- (void)childMethod:(NSArray *)params {
// some work
}
EDITED :
And Dont Forget to add childViewOBJ.childDelegate = self; at the time of create ChildViewController's object. such like,
childVC *cVC = [[childVC alloc]init];
cVC.childDelegate = self;
cVC.pVC = self;
[self presentModalViewController:cVC animated:YES];
For More information about How to create/use of Protocol.
First of all, you are not checking for the same selector as you declared in your protocol declaration so it won't respond to that. You declared the method childMethod: whereas you are checking if your childDelegate responds to myMethod: selector which does not so it won't go into the if condition.
Also the parent view controller is missing the implementation the method childMethod: in its .m. Implement that in your parent view controller or it will crash because of not finding the exact selector definition.
Since you are using a UINavigationController, the parent view controller won't be lost till the child view controller exist so the childDelegate property must not be strong unless you intend to hold onto your delegate in child view controller for some reason.
Alright, so this is an extension to a question I asked last night. I have a little firmer grasp on how data can be passed between view controllers using various techniques. I wanted to go the MVC route, and creating a Singleton class seems the closest concept similar to MVC.
Basically I created a simple app with two View Controllers and a singleton class. I am trying to pass the value of a text field into a UILabel. For whatever reason it isn't working. This is what my code looks like.
ViewController.h
#import <UIKit/UIKit.h>
#import "Model.h"
#import "ViewController2.h"
#interface ViewController : UIViewController {
NSString *text2pass;
}
#property (weak, nonatomic) IBOutlet UITextField *tf;
#property (weak, nonatomic) IBOutlet UILabel *btn;
- (IBAction)go:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize tf = _tf;
#synthesize btn = _btn;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *tfstring = _tf.text;
NSLog(#"string = %#",tfstring);
}
- (void)viewDidUnload
{
[self setTf:nil];
[self setBtn:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (IBAction)go:(id)sender {
NSLog(#"btn pressed");
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController2 *vc2 = (ViewController2 *) [storyboard instantiateViewControllerWithIdentifier:#"home"];
text2pass = _tf.text;
[self passValues];
[self presentModalViewController:vc2 animated:YES];
}
-(void) passValues {
Model *model = [Model sharedModel];
model.passedText = text2pass;
}
#end
ViewController2.h
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface ViewController2 : UIViewController {
NSString *passedText;
}
#property (nonatomic)NSString *passedValue;
#property (weak, nonatomic) IBOutlet UILabel *lbl;
- (IBAction)back:(id)sender;
#end
ViewController2.m
#import "ViewController2.h"
#interface ViewController2 () {
NSString *passedtext;
}
#end
#implementation ViewController2
#synthesize lbl = _lbl;
#synthesize passedValue = _passedValue;
- (void)viewDidLoad
{
// do code stuff here
NSLog(#"passedText = %#",passedText);
_lbl.text = passedText;
[super viewDidLoad];
}
- (void)viewDidUnload
{
[self setLbl:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (IBAction)back:(id)sender {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController *vc = (ViewController *) [storyboard instantiateViewControllerWithIdentifier:#"welcome"];
[self presentModalViewController:vc animated:YES];
}
#end
Model.h
#import <Foundation/Foundation.h>
#interface Model : NSObject {
NSString *passedText;
}
#property (nonatomic, strong) NSString* passedText;
+ (Model *) sharedModel;
#end
Model.m
#import "Model.h"
#implementation Model
#synthesize passedText = _passedText;
static Model *sharedModel = nil;
+ (Model *) sharedModel {
#synchronized(self){
if (sharedModel == nil){
sharedModel = [[self alloc] init];
}
}
return sharedModel;
}
#end
The project can be downloaded in its entirety from here http://chrisrjones.com/files/KegCop-Test.zip
If you know why the UILabel is not displaying the text field text let me know. Oh I pretty much followed this -> http://www.youtube.com/watch?v=ZFGgMPcwYjg&feature=plcp
Your addressing, and memory management is just plain... off. Firstly, there's absolutely no reason to create a singleton for this, but that's beside the point here.
Secondly, when declaring properties, (atomic, assign) is defaulted to if not otherwise specified, which means your string:
#property (nonatomic)NSString *passedValue;
is weak sauce, ripe for deallocation and destruction at a moments notice. Declare it copy, strong, or retain.
Thirdly, there's absolutely no reference to your singleton in the pushed view controller, yet you seem to have the belief that objects that are named the same in different classes retain their value (especially when #import'ed). Not so. You need to reference your singleton and pull the value of [Model sharedModel].passedText into that text field.
In fact, I fixed your sample in two lines:
//ViewController2.m
#import "ViewController2.h"
//actually import the singleton for access later
#import "Model.h"
#interface ViewController2 () {
NSString *passedtext;
}
#end
#implementation ViewController2
#synthesize lbl = _lbl;
#synthesize passedValue = _passedValue;
- (void)viewDidLoad
{
// do code stuff here
NSLog(#"passedText = %#",passedText);
//actually reference the singleton this time
_lbl.text = [Model sharedModel].passedText;
[super viewDidLoad];
}
- (void)viewDidUnload
{
[self setLbl:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (IBAction)back:(id)sender {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController *vc = (ViewController *) [storyboard instantiateViewControllerWithIdentifier:#"welcome"];
[self presentModalViewController:vc animated:YES];
}
#end
Which yields this:
I wouldn't recommend using a Singleton as a good way to pass data around your application. Most apps are simple enough that this kind of central access is not necessary, and it usually creates a maintenance nightmare... but I don't think the fact that you're using a Singleton is actually important to getting your code working.
Assuming you have access to the data in ViewController1, in your case through the a Singleton instance of Model (which needs a more descriptive name), then all you have to do is pass through the data to ViewController2 when it is created and presented, which eliminates the need for a Singleton at all.
Once you create the controller, set the data you need, and then present the view controller - which is basically what you're doing anyway.
As to why it's not working: Is the view controller being presented, just not with the correct data? Or is there actually an issue presenting the controller at all? I would set a breakpoint in the go: action of ViewController1, make sure the data you expect is in the textfield, correctly populates the Model and that the value is correctly pulled out of the Model in ViewController2.
Unless you've removed some of the code, it looks like you correctly populate the Model property in ViewController1, but in ViewController2 you refer to a local ivar passedTextrather than pulling it from the model.
On a separate note, the way to go back from a presented modal view controller is usually to dismiss that controller, not to re-create the initial controller and present that over the top.