Pass an array from ViewController to AppDelegate - ios

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

Related

Need direction about Binary Search Tree implementation in Objective-C

I have a partial implementation of a binary tree that doesn't work properly. I believe I am missing fundamental knowledge about struct memory management in objective-c but not sure what it is(besides malloc). When I try to create a new tree node based on a struct I get
Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
which led me to believe I didn't create a memory location for this struct pointer. What is the proper way of doing this in Objective-C? (Code in below)
Thank you for taking the time to respond. The code seems correct from the logic perspective so not sure what the issue is here.
EDIT
I've modified the source code based on #trungduc 's response. But now I am getting a stack overflow in the printDescription method
issue:
Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffeef3fffe8) // on line [self printDescription:root.left];
PS.
I did see this question but didn't help. I also saw this repo but I am not sure happy with some of the implementation details so I ended up not following it. Does anyone know any good guides/tutorials on how to do trees and graphs in Objective-C?
Main.m
#import <Foundation/Foundation.h>
// BSTNode is an Objective-C class
#interface BSTNode : NSObject
#property (nonatomic, assign) int data;
#property (nonatomic, strong) BSTNode *left;
#property (nonatomic, strong) BSTNode *right;
#end
#implementation BSTNode
#end
#interface BST: NSObject
- (BSTNode *)insertNode:(BSTNode *)root withData:(int)data;
- (void)printDescription:(BSTNode *)root;
#end
#implementation BST
- (BSTNode *)initializeTreeNode {
// By default, |data| is 0, |left| is nil, |right| is nil
return [[BSTNode alloc] init];
}
- (BSTNode *)insertNode:(BSTNode *)root withData:(int)data {
if(!root) {
root = [self initializeTreeNode];
root.data = data;
} else if (root.data >= data) {
root.left = [self insertNode:root.left withData:data];
} else {
root.right = [self insertNode:root.right withData:data];
}
return root;
}
- (void)printDescription:(BSTNode *)root {
// in order left - root - right
[self printDescription:root.left];
NSLog(#"%d",root.data);
[self printDescription:root.right];
}
#end
and inside the main method:
int main(int argc, const char * argv[]) {
#autoreleasepool {
BST *bst = [[BST alloc] init];;
BSTNode *root = [[BSTNode alloc]init];
[bst insertNode:root withData:20];
[bst insertNode:root withData:15];
[bst insertNode:root withData:25];
[bst printDescription:root];
}
return 0;
}
You got crash because you called node->data while node is NULL.
In this case, I suggest to define BSTNode as an Objective-C class. You can try my code below.
// BSTNode is an Objective-C class
#interface BSTNode : NSObject
#property (nonatomic, assign) int data;
#property (nonatomic, strong) BSTNode *left;
#property (nonatomic, strong) BSTNode *right;
#end
#implementation BSTNode
#end
#interface BST: NSObject
- (BSTNode *)insertNode:(BSTNode *)root withData:(int)data;
- (void)printDescription:(BSTNode *)root;
#end
#implementation BST
- (BSTNode *)initializeTreeNode {
// By default, |data| is 0, |left| is nil, |right| is nil
return [[BSTNode alloc] init];
}
- (BSTNode *)insertNode:(BSTNode *)root withData:(int)data {
if(!root) {
root = [self initializeTreeNode];
root.data = data;
} else if (root.data >= data) {
root.left = [self insertNode:root.left withData:data];
} else {
root.right = [self insertNode:root.right withData:data];
}
return root;
}
- (void)printDescription:(BSTNode *)root {
if (!root) {
return;
}
// in order left - root - right
[self printDescription:root.left];
NSLog(#"%d",root.data);
[self printDescription:root.right];
}
#end

Passing Data from VC to VC and Model

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)

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

Referencing a ViewController in AppDelegate - Objective C

I am trying to get a NSMutableArray from another class. I am trying to reference that ViewController that contains the array. Lets say, a has the array I want. b is the class I am going to get that array in.
a.h:
#property (strong, nonatomic) NSMutableArray *selectedCells;
a.m:
#synthesize selectedCells;
AppDelegate.h:
#property (strong, nonatomic) a *create_challengeDelegate;
AppDelegate.m:
#synthesize create_challengeDelegate;
a *create_challengeDelegate = [[a alloc]init];
Right here when I try to reference that ViewController I get an error saying:
Initializer element is not a compile-time constant
I assume it has something to do with it not being able to see the ViewController.
In my b.m:
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
app.create_challengeDelegate.selectedCells
My issue is working with initializing the ViewController in the delegate.
Suggestions and thoughts on that?
My suggestion is that you create the selectedCells array in you AppDelegate and send it to A. E.g. in:
AppDelegate.h
#property (nonatomic, strong) NSArray *selectedCells;
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.selectedCells = [NSMutableArray array];
A *viewA = [[a alloc] initWithNibName:#"a" bundle:nil selectedCells:self.selectedCells];
[...]
}
a.h
#property (nonatomic, strong) NSMutableArray *selectedCells;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil selectedCells:(NSMutableArray*)cells;
a.m
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil selectedCells:(NSMutableArray*)cells
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.selectedCells = cells;
}
return self;
}
b.m
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSMutableArray *selectedCells = app.selectedCells;
This way, whenever you make changes to selectedCells it will keep it in the array you send there since selectedCells is a refference to an object which is created in your AppDelegate.
Otherwise, what you are trying to do is access a view which might no longer be available in the memory since the view might have been deallocated.
Also, you create the instance of a but its not set to the AppDelegate's instance of a, its a seperate instance.
Instead of
#synthesize create_challengeDelegate;
a *create_challengeDelegate = [[a alloc]init];
You should have
#synthesize create_challengeDelegate;
self.create_challengeDelegate = [[a alloc] init];
I still strongly recommend you do not access your view this way though.
P.s. #synthesize is no longer necessary.
Edit
This is a better solution.
Selection.h
/**
* The entity for selections
*/
#interface Selection : NSObject
/**
* The Shared Instance
*
* #return Selection The instance
*/
+ (Selection *)sharedInstance;
/**
* Add an item to the selections
*
* #param object id The object to add
*/
- (void)addSelection:(id)object;
/**
* Remove an item from the selections
*
* #param object id The object to remove
*/
- (void)removeObject:(id)object;
/**
* Get the selections
*
* #return NSArray The array with the current selection objects
*/
- (NSArray *)getSelections;
#end
Selection.m
#import "Selection.h"
#interface Selection ()
#property (nonatomic, strong) NSMutableArray *selections;
#end
#implementation Selection
#pragma mark - Public methods
+ (Selection *)sharedInstance
{
static Selection *sharedInstance = nil;
#synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[self alloc] init];
}
}
return sharedInstance;
}
#pragma mark - Private methods
- (id)init
{
if (self = [super init])
{
self.selections = [NSMutableArray array];
}
return self;
}
#pragma mark - Manage selection
- (void)addSelection:(id)object
{
[self.selections addObject:object];
}
- (void)removeObject:(id)object
{
[self.selections removeOjbect:object];
}
- (NSArray *)getSelections
{
return [NSArray arrayWithArray:self.selections];
}
Now you can assess your shared singeton in A and/or B by accessing:
[[Selection sharedInstance] addObject:myObject];
[[Selection sharedInstance] removeObject:myObject];
NSArray *array = [[Selection sharedInstance] getSelections];
Is this line inside a method:
a *create_challengeDelegate = [[a alloc]init];
If not that would probably explain your issue. If that's not the problem it might be helpful to post more complete code.

override property from superclass in subclass

I want to override an NSString property declared in a superclass. When I try to do it using the default ivar, which uses the the same name as the property but with an underscore, it's not recognised as a variable name. It looks something like this...
The interface of the superclass(I don't implement the getter or setter in this class):
//Animal.h
#interface Animal : NSObject
#property (strong, nonatomic) NSString *species;
#end
The implementation in the subclass:
//Human.m
#implementation
- (NSString *)species
{
//This is what I want to work but it doesn't and I don't know why
if(!_species) _species = #"Homo sapiens";
return _species;
}
#end
Only the superclass has access to the ivar _species. Your subclass should look like this:
- (NSString *)species {
NSString *value = [super species];
if (!value) {
self.species = #"Homo sapiens";
}
return [super species];
}
That sets the value to a default if it isn't currently set at all. Another option would be:
- (NSString *)species {
NSString *result = [super species];
if (!result) {
result = #"Home sapiens";
}
return result;
}
This doesn't update the value if there is no value. It simply returns a default as needed.
to access the superclass variables, they must be marked as #protected, access to such variables will be only inside the class and its heirs
#interface ObjectA : NSObject
{
#protected NSObject *_myProperty;
}
#property (nonatomic, strong, readonly) NSObject *myProperty;
#end
#interface ObjectB : ObjectA
#end
#implementation ObjectA
#synthesize myProperty = _myProperty;
#end
#implementation ObjectB
- (id)init
{
self = [super init];
if (self){
_myProperty = [NSObject new];
}
return self;
}
#end

Resources