Darin
Darin

Reputation: 29

Calling method from within another class?

I have a couple of views, one being the SettingsMenu the other being Game .

I have initialised backgroundMusic from within Game. When there is a change in SettingsMenu, I'd like it to run backgroundMusicStop in settingsMenu, however backgroundMusicStop is part of my Game class.

Game.h

-(void)backgroundMusicStop;

Game.m

-(void)backgroundMusicStop {
    [backgroundMusic stop];
    backgroundMusic.currentTime = 0;
}

SettingsMenu.m

-(IBAction)musicOptionSwitch:(id)sender {
    if (backgroundMusicPlay == YES) {
        backgroundMusicPlay = NO;
        [Game backgroundMusicStop];
    }
}

I've looked into it and I don't understand how to fix it, I know I need to make the method accessible from all classes, but I am confused on how to do that, any help would be greatly appreciated.

Upvotes: 0

Views: 52

Answers (2)

Phillip Mills
Phillip Mills

Reputation: 31016

I think the best architecture for what you're trying to do would be:

  • create one instance of a Settings object and have it keep track of the current state of each option you want the user to control

  • update those values from inside your SettingsMenu

  • have your Game listen for changes to the Settings values

The advantage in this is that your view controllers only communicate through changes to the data model (Settings) rather than having to know about each other. Your Settings object can either be a singleton or a normal object that you get from your app delegate.

First create the settings data model:

// Settings.h
#import <Foundation/Foundation.h>

@interface Settings : NSObject

// A singleton so that the example stays simple
+ (instancetype)sharedSettings;

// YES/NO.  Add other properties as needed.
@property (nonatomic, assign) BOOL musicShouldPlay;
#define MUSIC_SHOULD_PLAY @"musicShouldPlay"

@end

// Settings.m
#import "Settings.h"

@implementation Settings

// Guarantee only one instance
+ (instancetype)sharedSettings {
    static dispatch_once_t onceToken;
    static Settings *result = nil;
    dispatch_once(&onceToken, ^{
        result = [[Settings alloc] init];
    });
    return result;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _musicShouldPlay = NO;
    }
    return self;
}

@end

The controller that makes the changes:

// SettingsViewController.m
#import "SettingsViewController.h"
#import "Settings.h"

@interface SettingsViewController ()

@property (strong, nonatomic) IBOutlet UISwitch *musicSwitch;

@end

@implementation SettingsViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // Make the display consistent with previous settings
    [self.musicSwitch setOn:[[Settings sharedSettings] musicShouldPlay]];
}

// I'm presenting this using a modal segue from a "Change settings" button on the main controller,
// so it has a "Done" button
- (IBAction)done:(id)sender {
    [ self dismissViewControllerAnimated:YES completion:nil];
}

// Switch value changes are connected to this in the storyboard
- (IBAction)changePlayState:(id)sender {
    [[Settings sharedSettings] setMusicShouldPlay:[self.musicSwitch isOn]];
}

@end

The controller that reacts to changes:

// ViewController.m
#import "ViewController.h"
#import "Settings.h"

@implementation ViewController

// Since this is the primary controller, I'll have it listen for its lifetime.
- (void)viewDidLoad {
    [super viewDidLoad];
    [[Settings sharedSettings] addObserver:self
                                forKeyPath:MUSIC_SHOULD_PLAY
                                   options:NSKeyValueObservingOptionNew
                                   context:nil];
}

- (void)dealloc {
    [[Settings sharedSettings] removeObserver:self forKeyPath:MUSIC_SHOULD_PLAY];
}

// Here's where the KVO notifications are delivered.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:MUSIC_SHOULD_PLAY]) {
        NSNumber *newValue = [change valueForKey:NSKeyValueChangeNewKey];
        if ([newValue integerValue] == 1) {
            NSLog(@"Request to turn music on");
        } else {
            NSLog(@"Request to turn music off");
        }
    }
}

@end

Upvotes: 1

Novarg
Novarg

Reputation: 7440

The problem is in this line of code:

[Game backgroundMusicStop];

In your implementation, Game is a class name and you try to call an instance method on a class. There are 2 options to fix it:

  1. Create a new Game instance
  2. Make backgroundMusicStop a class method

To create a Game instance you would need something like:

Game myGame = [[Game alloc]init];
//depending on your implementation it could be different

Or if you choose for second option you will have to change

-(void)backgroundMusicStop;

to

+(void)backgroundMusicStop;

I would also suggest you to find some book/website to understand class and instance variables/methods better and more in-depth.

Upvotes: 2

Related Questions