Passing Data from VC to VC and Model - ios

I have a levelViewController:
.m
- (void)viewDidLoad {
[super viewDidLoad];
double delayTimeInSeconds = 2;
dispatch_time_t popTimeDelay = dispatch_time(DISPATCH_TIME_NOW, delayTimeInSeconds * NSEC_PER_SEC);
dispatch_after(popTimeDelay, dispatch_get_main_queue(), ^(void){
//code to be executed after delay
GameViewController *level1 = [self.storyboard instantiateViewControllerWithIdentifier:#"gameViewController"];
level1.CARDS_PER_ROW = 6;
level1.gameModel.NUMBER_OF_PAIRS = 3;
[self presentViewController:level1 animated:NO completion:nil];
});//code to be executed after delay end
}
ok, he presents now the GameViewController with level1. The CARDS_PER_ROW is declared in GameViewController.h
.h
#import <UIKit/UIKit.h>
#import "GameModel.h"
#interface GameViewController : UIViewController
#property (nonatomic) GameModel *gameModel;
#property (nonatomic) int CARDS_PER_ROW;
#property (nonatomic) NSInteger newGame;
#end
.m
in my ViewDidLoad i have
self.gameModel = [[GameModel alloc] initWithNewGame:self.newGame];
And for my GameViewController i have my GameModel:
.h
#import <Foundation/Foundation.h>
#interface GameModel : NSObject
#property (nonatomic) int NUMBER_OF_PAIRS;
-(id) initWithNewGame: (NSInteger)newGame;
#end
.m
-(NSMutableArray *) createNewDeck {
NSMutableArray *cardDeck = [NSMutableArray array];
for (int i = 0; i < _NUMBER_OF_PAIRS; i++) {
[cardDeck addObject:[[CardModel alloc] initWithValue:i]];
[cardDeck addObject:[[CardModel alloc] initWithValue:i]];
}
return cardDeck;
}
Ok. My Problem is:
I CAN passing CARDS_PER_ROW. If i define NUMBER_OF_PAIRS ( #define NUMBER_OF_PAIRS = x; ) it works. But i CANT passing my NUMBER_OF_PAIRS data, and i don't know why. The Screen is empty with this Code I've posted.

The problem is that gameModel is nil at that point. The fact that you have instantiated a controller doesn't mean that its view is also loaded, the view will be lazy-loaded at first access. So, what you can do is to move the gameModel initializer to the controller's initializer (-initWithCoder in case of a nib/storyboard instantiation)

Related

How to Pass Data Object to View Controller Embedded in Navigation Controller in Different Storyboard

My code which normally works fine for passing an object to another view controller is not working when the view controller is in a different storyboard.
My code loads the correct view controller embedded in its navigation controller but without any data. (The data object is nil in the destination VC).
Here is the code I'm trying to use;
UIStoryboard *sb2 = [UIStoryboard storyboardWithName:#"secondSB" bundle:nil];
UINavigationController* nav = [sb2 instantiateViewControllerWithIdentifier:#"userNav"];
userDetail *destVC = (userDetail * )nav.topViewController;
NSLog(#"user name%#",user.name);//Logs name showing the user is not empty
destVC.user = user;
[self presentViewController:nav animated:YES completion:nil];
The above loads a VC with no data.
I am able to pass the data object to the VC if I present the VC directly without the navigation controller. But in that case, I lose the navigation functionality which I need.
UIStoryboard *sb2 = [UIStoryboard storyboardWithName:#"secondSB" bundle:nil];
userDetail *destVC = [sb2 instantiateViewControllerWithIdentifier:#"userDetail"];
NSLog(#"user name%#",user.name);//Logs name showing the user is not empty
destVC.user = user;
[self presentViewController:destVC animated:YES completion:nil];
What could be wrong with the above code and what code should I use.
Edit:
I am able to pass a regular object such as a String to the VC embedded in the nav. Or I can pass a custom object to the VC when it is not embedded in the nav. I just can't pass a custom object such as user or I created another NSObject for testing to the VC when embedded in a nav. Perhaps this is some weird glitch when using a different storyboard.
Edit 2
Here is the object code for a light version of user I created in case there was something wrong with the original user object:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#interface lightUser : NSObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSNumber * userid;
#property (nonatomic, retain) NSString * descript;
#end
It is a property in the VC:
#import "lightServer.h"
//in interface
#property (weak, nonatomic) lightUser* user;
The following code in ViewDidLoad does not have any effect and the user appears as nil:
self.user.name = #"Hello there";//
po self.user.name in debugger shows nil
po self.user in debuggers shows nil
Not sure without seeing a full example of your code, but I you must be missing something...
Here is a complete example. It should be obvious what gets connected to #IBOutlet and #IBAction (and Storyboard IDs):
UserObject.h
//
// UserObject.h
// Created by Don Mag on 4/1/20.
//
#import <Foundation/Foundation.h>
#interface UserObject : NSObject
#property (strong, nonatomic) NSString *firstName;
#property (strong, nonatomic) NSString *lastName;
#property (assign, readwrite) NSInteger age;
- (NSString *)name;
#end
UserObject.m
//
// UserObject.m
// Created by Don Mag on 4/1/20.
//
#import "UserObject.h"
#implementation UserObject
- (NSString *)name {
return [NSString stringWithFormat:#"%#, %#", _lastName, _firstName];
}
#end
** FirstViewController.h**
//
// FirstViewController.h
// Created by Don Mag on 4/1/20.
//
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController
#end
** FirstViewController.m**
//
// FirstViewController.m
// Created by Don Mag on 4/1/20.
//
#import "FirstViewController.h"
#import "UserDetailViewController.h"
#import "UserObject.h"
#interface FirstViewController ()
#property (strong, nonatomic) UserObject *aUserObject;
#property (assign, readwrite) NSInteger iAge;
#end
#implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
// initialize age
_iAge = 25;
// initialize a new UserObject
_aUserObject = [UserObject new];
_aUserObject.firstName = #"John";
_aUserObject.lastName = #"Smith";
_aUserObject.age = _iAge;
}
- (IBAction)didTap:(id)sender {
UIStoryboard *sb2 = [UIStoryboard storyboardWithName:#"secondSB" bundle:nil];
UINavigationController* nav = [sb2 instantiateViewControllerWithIdentifier:#"userNav"];
UserDetailViewController *destVC = (UserDetailViewController * )nav.topViewController;
// increment age, so it changes each time we call this method
_iAge++;
_aUserObject.age = _iAge;
destVC.userObj = _aUserObject;
[self presentViewController:nav animated:YES completion:nil];
}
#end
UserDetailViewController.h (VC is in second storyboard)
//
// UserDetailViewController.h
// Created by Don Mag on 3/31/20.
//
#import <UIKit/UIKit.h>
#import "UserObject.h"
#interface UserDetailViewController : UIViewController
#property (strong, nonatomic) UserObject *userObj;
#end
UserDetailViewController.m
//
// UserDetailViewController.m
// Created by Don Mag on 3/31/20.
//
#import "UserDetailViewController.h"
#interface UserDetailViewController ()
#property (strong, nonatomic) IBOutlet UILabel *userLabel;
#end
#implementation UserDetailViewController
- (void)viewDidLoad {
[super viewDidLoad];
_userLabel.text = [NSString stringWithFormat:
#"_useObj.firstName: %# \n" \
"_userObj.lastName: %# \n" \
"_userObj.age: %ld \n" \
"_userObj name method: %#",
_userObj.firstName,
_userObj.lastName,
_userObj.age,
[_userObj name]];
}
#end
In case it's not completely clear, here is a working example app: https://github.com/DonMag/DataObjectPassing

Pass an array from ViewController to AppDelegate

I have an array of floating point values called playbackRates, set in a ViewController. I want to use these values in a function within my appdelegate. How would I access these values from within my appdelegate?
ViewController.h
#interface P15ViewController : UIViewController <GuitarStringsViewDelegate>
{
float playbackRates[6];
}
ViewController.m
- (void)viewDidLoad
{
self.guitarStringView.delegate = self;
chordNameLabel.text = chordName;
self.guitarStringView.chordName=chordName;
if([chordName isEqualToString:#"A"]){
playbackRates[0] = 1.0;
playbackRates[1] = 1.0;
playbackRates[2] = 2 * pow(2, (2/12));
playbackRates[3] = 2 * pow(2, (2/12));
playbackRates[4] = 2 * pow(2, (2/12));
playbackRates[5] = 1.0;
}
else if([chordName isEqualToString:#"B"]){
//set rates
}
else if([chordName isEqualToString:#"C"]){
//set rates
}
[super viewDidLoad];
}
appdelegate.h
#import <UIKit/UIKit.h>
#import <PAEEngine/PAEEngine.h>
#interface P15AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
-(void)play:(int)index velocity:(float)velocity;
#end
appdelegate.m - player.rate needs to be set by each value in the array within the for loop in the play method
#import "P15AppDelegate.h"
#import "P15ViewController.h"
#interface P15AppDelegate ()
#property (nonatomic, strong) PAEAudioHost* host;
#property (nonatomic, strong) NSArray* channelStrips;
#property (nonatomic, strong) NSArray* filemnames;
#property (nonatomic) int nextVoice;
#end
#implementation P15AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.host = [PAEAudioHost audioHostWithNumOutputs:2];
self.filemnames = #[#"guitar-E1.wav", #"guitar-A2.wav", #"guitar-D3.wav",
#"guitar-G3.wav", #"guitar-B3.wav", #"guitar-E4.wav"];
const int numVoices = 8;
NSMutableArray* channelStrips = [[NSMutableArray alloc] initWithCapacity:numVoices];
for (int i = 0; i < numVoices; ++i)
{
PAEChannelStrip* channelStrip = [PAEChannelStrip channelStripWithNumChannels:2];
[channelStrips addObject:channelStrip];
}
self.channelStrips = [NSArray arrayWithArray:channelStrips];
PAEAmplitude* amp = [PAEAmplitude amplitudeWithNumInputs:2];
amp.input = [PAEMixer mixerWithSources:self.channelStrips];
amp.level.input = [PAEConstant constantWithValue:1.0 / numVoices];
self.host.mainMix = amp;
[self.host start];
return YES;
}
-(void)play:(int)index velocity:(float)velocity;
{
if (index >= 0 && index < self.filemnames.count)
{
PAEAudioFilePlayer* player = [PAEAudioFilePlayer audioFilePlayerNamed:self.filemnames[index]];
player.loop = NO;
player.rate - playbackRates[i]; --> **NEED TO ACCESS ARRAY HERE**
PAEAmplitude* amp = [PAEAmplitude amplitudeWithNumInputs:player.numChannels];
amp.input = player;
amp.level.input = [PAEConstant constantWithValue:velocity];
PAEChannelStrip* channelStrip = self.channelStrips[self.nextVoice];
channelStrip.input = amp;
self.nextVoice++;
if (self.nextVoice == self.channelStrips.count)
self.nextVoice = 0;
}
}
#end
First you'll need to write a public accessor for the playbackRates array in your UIViewController subclass named P15ViewController.
P15ViewController.h add -
- (float)playbackRateAtIndex:(NSInteger)index;
P15ViewController.m add -
- (float)playbackRateAtIndex:(NSInteger)index {
return playbackRates[index];
}
Your appDelegate needs to know of this method so import the P15ViewController.h in your P15AppDelegate.m, which I see you've already done.
Then assuming the viewController is the rootViewController of the appDelegate then you can access it with the following code.
Replace -
player.rate - playbackRates[i]; --> **NEED TO ACCESS ARRAY HERE**
With -
player.rate = [(P15ViewController*)self.window.rootViewController playbackRateAtIndex:i];
The typecast is needed so as to enable the calling of the accessor (more specifically a getter).
If a more direct access way is needed then change the getter or add another getter to return the typed address of the playbackRates array like this in the P15ViewController.m -
- (float*)playbackRatesArray {
return &playbackRates;
}
Remember to update the header file also!
And access it like this from P15AppDelegate.m, since a C array is just a pointer -
float* playbackRates = [(P15ViewController*)self.window.rootViewController playbackRatesArray];
player.rate = playbackRates[i];

How to access property in different view controller?

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];
}

Passing Data Through ViewControllers Not Working

Ok so I am trying to pass data from one view controller to another via the following code but its not working and I have no idea why.
In my ViewController.m I have imported ViewControllerTwo.h and declared ViewControllerTwo:
#import "ViewController.h"
#import "ViewControllerTwo.h"
#interface ViewController ()
#end
#implementation ViewController
{
ViewControllerTwo *settings;
}
#synthesize blockSizeLB;
#synthesize wpmLB;
#synthesize textTV;
#synthesize slider;
#synthesize stepper;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//sets label to corresponding value
wpmLB.text = [NSString stringWithFormat:#"WPM: %#", [[NSNumber numberWithInt:slider.value] stringValue]];
//configure stepper and its label to default values of 1
stepper.value = 1;
blockSizeLB.text = [NSString stringWithFormat:#"Block Size: %#", [[NSNumber numberWithInt:stepper.value] stringValue]];
//sets properties for next ViewController
settings = [[ViewControllerTwo alloc] init];
settings.timerValue = 60 / slider.value;
settings.text = textTV.text;
settings.blockCount = stepper.value;
}
In ViewControllerTwo.h I have:
#property (nonatomic) float timerValue;
#property (nonatomic) NSString * text;
#property (nonatomic) int blockCount;
Later on in the ViewController.m I need to change the properties defined in ViewControllerTwo:
Method from ViewController.m. This is also done earlier in my viewDidLoad to set the default values of the properties:
- (IBAction)sliderSlide:(id)sender
{
//event when the slider value is changed
//rounds value to nearest increment of 5
int increment = 5 * floor(((int)slider.value/5)+0.5);
[slider setValue:increment animated:YES];
//changes WPM: 0 label text
wpmLB.text = [NSString stringWithFormat:#"WPM: %#", [[NSNumber numberWithInt:increment] stringValue]];
//sets properties for next ViewController
settings.timerValue = 60 / slider.value;
}
I try to test if this is successful by calling a method in ViewControllerTwo.m that logs its property blockCount via NSLog. The output however is (null) meaning I was unsuccesful in passing the data from ViewController.m to ViewControllerTwo
If you are using segues, you should be doing this inside of:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
Then name your segue, then do something like this:
if([segue.identifier isEqual: #"segue_From_1_To_2"])
{
ViewControllerTwo *vc2 = segue.destinationViewController;
vc2.timerValue = 123.45;
vc2.text = #"whatever";
vc2.blockCount = 1;
}
You can create a custom initializer in your viewControllerTwo. name it initWithTimerValue:
- (id)initWithTimerValue:(CGFloat)timerValue
{
self = [super init];
if (self) {
self.timerValue = timerValue;
}
return self;
}

How to initiate a view controller from another view controller?

I have a table view controller called StackViewController, this is where I hold a list of todo's that has been created in CreateViewController...
I have an NSString property in StackViewController called currentTarget that represent the first to do in the stack:
import <UIKit/UIKit.h>
#interface StackTableViewController : UITableViewController
#property (nonatomic, strong) NSString *currentTarget;
#end
This property holds the first NSString object in the table view, I get it like this:
#import "StackTableViewController.h"
#import "Target.h"
#import "CoreDataStack.h"
#interface StackTableViewController () <NSFetchedResultsControllerDelegate>
#property (nonatomic, strong) NSFetchedResultsController *fetchedResultController;
#end
#implementation StackTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.fetchedResultController performFetch:nil];
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
Target *target = [self.fetchedResultController objectAtIndexPath:indexPath];
self.currentTarget = target.body;
}
Now, when I log into my home page which called HomeViewController I want to initiate the StackTableViewController and get its currentTatget property value...
I know that there are delegate to help you notify other views when a change has happened, but in my case I want to get this property value before even I have been in this page (StackTableViewController), because the HomeViewController is the first view controller that is loaded (my initial view controller) and I what to access this property when I was just logged in to the app and populate a label with it.
How should I do this?
I thought maybe something like this:
#import "HomeViewController.h"
#import "CreateViewController.h"
#import "StackTableViewController.h"
#interface HomeViewController ()
#property (strong, nonatomic) IBOutlet UILabel *targetLabel;
#end
#implementation HomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
StackTableViewController *vc = [[StackTableViewController alloc] init];
NSString *current = vc.currentTarget;
self.targetLabel.text = current;
}
But i'm missing something here...my label is not populated...
I think there is something with the views lifecycle.
i'm a newbie please help me to figure this out...thanks
Don't do anything to do with graphics in viewDidLoad. The earliest you want to do it is in viewWillAppear (most of the time) and occasionally you will need to do it in viewDidAppear.
Try the same code in viewWillAppear and it should work.
Oh right, since your other viewController is setup in viewDidLoad, you need to call
[stackTableViewController view] on your stackTableViewController after you alloc init it. Seems weird, but this actually works. This is because the StackTableViewController doesn't have its calculation done when you initialize it, it runs through it in it's viewDidLoad delegate.
- (void)viewDidLoad {
[super viewDidLoad];
StackTableViewController *vc = [[StackTableViewController alloc] init];
[vc view];
NSString *current = vc.currentTarget;
self.targetLabel.text = current;
}

Resources