【问题标题】:UILabel being referenced as an integer pointer in xcode [closed]UILabel 在 xcode 中被引用为整数指针 [关闭]
【发布时间】:2014-12-15 15:35:25
【问题描述】:

目标和问题:

每经过一秒,“regularBubbleCount”的值(RWGameData 的一个变量)就会增加 1。我试图通过将“regularBubbleCount”的新值传递给“regularBubLabel”UILabel 中的主视图控制器。我正在尝试使用以下代码行来做到这一点,

_regularBubLabel.text = [NSString stringWithFormat:@"%li", [RWGameData sharedGameData].regularBubbleCount];

显然这不起作用,因为 'regularBubLabel' 不是 'timerCalled' 方法所在的 RWGameData 类的对象。如何从 RWGameData 类中更改“regularBubLabel”的值?

RWGameData.h

#import <Foundation/Foundation.h>

@interface RWGameData : NSObject <NSCoding>

@property (assign, nonatomic) long regularBubbleCount;

@property (assign, nonatomic) BOOL dataIsInitialized;

+(instancetype)sharedGameData;
-(void)reset;
-(void)save;

-(void)timerSetup;
-(void)timerCalled;

@end

RWGameData.m

#import "RWGameData.h"

@implementation RWGameData

static NSString* const SSGameDataRegularBubbleCountKey = @"regularBubbleCount";
static NSString* const SSGameDataIsInitializedKey = @"dataIsInitializedKey";

+ (instancetype)sharedGameData {
    static id sharedInstance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [self loadInstance];
    });

    return sharedInstance;
}

-(void)reset {
    self.regularBubbleCount = 0;

    self.dataIsInitialized = true;
}

- (void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeDouble:self.regularBubbleCount forKey: SSGameDataRegularBubbleCountKey];

    [encoder encodeBool:self.dataIsInitialized forKey: SSGameDataIsInitializedKey];
}

- (instancetype)initWithCoder:(NSCoder *)decoder
{
    self = [self init];
    if (self) {
        _regularBubbleCount = [decoder decodeDoubleForKey: SSGameDataRegularBubbleCountKey];

        _dataIsInitialized = [decoder decodeBoolForKey: SSGameDataIsInitializedKey];
    }
    return self;
}

+(NSString*)filePath
{
    static NSString* filePath = nil;
    if (!filePath) {
        filePath =
        [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]
         stringByAppendingPathComponent:@"gamedata"];
    }
    return filePath;
}

+(instancetype)loadInstance
{
    NSData* decodedData = [NSData dataWithContentsOfFile: [RWGameData filePath]];
    if (decodedData) {
        RWGameData* gameData = [NSKeyedUnarchiver unarchiveObjectWithData:decodedData];
        return gameData;
    }

    return [[RWGameData alloc] init];
}

-(void)save
{
    NSData* encodedData = [NSKeyedArchiver archivedDataWithRootObject: self];
    [encodedData writeToFile:[RWGameData filePath] atomically:YES];
}

- (void)timerSetup { // to be called from delegate didFinishLaunching….
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerCalled) userInfo:nil repeats:YES];
}

-(void)timerCalled
{
            [RWGameData sharedGameData].regularBubbleCount++;
            /* THE ISSUE IS HERE */
            _regularBubLabel.text = [NSString stringWithFormat:@"%li", [RWGameData sharedGameData].regularBubbleCount];
            [[RWGameData sharedGameData] save];

    } NSLog(@"Regular Bubble Count: %li", [RWGameData sharedGameData].regularBubbleCount);
}

@end

PrimaryViewController.h

#import <UIKit/UIKit.h>
#import "RWGameData.h"

@interface PrimaryViewController : UIViewController

@property (strong, nonatomic) IBOutlet UILabel *regularBubLabel;

@end

PrimaryViewController.m

#import "PrimaryViewController.h"

@interface PrimaryViewController ()

@end

@implementation PrimaryViewController
{
    NSString *bubbleImage;
    UIImage *backgroundImage;
    UIImageView *backgroundImageView;
    int r;
    int i;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    backgroundImage = [UIImage imageNamed:@"background_new.png"];
    backgroundImageView=[[UIImageView alloc]initWithFrame:self.view.frame];
    backgroundImageView.image=backgroundImage;
    [self.view insertSubview:backgroundImageView atIndex:0];

}

- (void)viewDidAppear:(BOOL)animated {
    _regularBubLabel.text = [NSString stringWithFormat:@"%li", [RWGameData sharedGameData].regularBubbleCount];
    _premiumBubLabel.text = [NSString stringWithFormat:@"%li", [RWGameData sharedGameData].premiumBubbleCount];

}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)increment {
    if ([RWGameData sharedGameData].megaBubblePopValue == 0) {
        [RWGameData sharedGameData].megaBubblePopValue++;
        [[RWGameData sharedGameData] save];
    } 

    if ([@"mysterybubble.png" isEqual:bubbleImage]) {
        [RWGameData sharedGameData].premiumBubbleCount += 2;
        _premiumBubLabel.text = [NSString stringWithFormat:@"%li", [RWGameData sharedGameData].premiumBubbleCount];
    } else if ([@"megaBubbleLarge30.png" isEqual:bubbleImage]) {
        [RWGameData sharedGameData].regularBubbleCount += [RWGameData sharedGameData].megaBubblePopValue;
        _regularBubLabel.text = [NSString stringWithFormat:@"%li", [RWGameData sharedGameData].regularBubbleCount];
    } i++;
}

- (IBAction)save {
    [[RWGameData sharedGameData] save];
}

- (IBAction)setBubbleStatus {
    r = arc4random_uniform(400);

    if (r <= 1) {
        bubbleImage = @"mysterybubble.png";
        [_megaBubbleButton setImage:[UIImage imageNamed:bubbleImage] forState:UIControlStateNormal];
        NSLog(@"Roll SUCCESS. [%i] %i", i, r);
    } else {
        bubbleImage = @"megaBubbleLarge30.png";
        [_megaBubbleButton setImage:[UIImage imageNamed:bubbleImage] forState:UIControlStateNormal];
        NSLog(@"Roll FAIL. [%i] %i", i, r);
    }
}

@end

【问题讨论】:

  • 我很感激投反对票 - 我会在未来努力让我的问题变得更强大 - 但如果你有任何信息可供我使用,我也会感谢你在投反对票的同时发表评论.
  • 我强烈建议您将问题集中在所需的代码上。
  • 请删除除相关代码之外的所有代码,并指出导致问题的确切代码行。
  • 我有几个问题。一、RWGameData和PrimaryViewController在哪里实例化? RWGameData::regBubleLabel 的更新是否发生在主线程上?
  • @ReinhardMänner 我已经适当地更新了主题。谢谢!

标签: ios objective-c iphone xcode


【解决方案1】:

在 RWGameData 类中包含 IBOutlet 是没有意义的,因为它是 NSObject 的子类。该类在情节提要中没有实例(也没有视图),因此您无法连接 IBOutlet。实现目标的一种方法是使 PrimaryViewController 成为 RWGameData 的委托,并在计时器的操作方法中调用委托方法,该方法将传递您需要的任何数据,以便 PrimaryViewController 可以更新自己的标签。

【讨论】:

  • 为了清楚起见,可以实例化一个自定义类并在情节提要中连接其出口,即使在可能是糟糕设计的情况下(例如OP提出的那个)。
  • 明白。因此,我将放弃该方法并尝试实施您提出的方法。我将四处寻找有关如何使 PrimaryViewController 成为 RWGameData 的委托以及如何从计时器的操作方法内部调用委托方法的一些信息。
  • @jlehr,是的,这在技术上是正确的。您可以将通用对象拖入情节提要,并将其更改为您的自定义对象。除非您导入 UIKit,否则 OP 的代码甚至不会编译,因为 NSObject 对标签一无所知。
【解决方案2】:

我将做出以下假设:

假设 #1 -RWGameData 在应用程序加载时在 ApplicationDelegate 内部实例化,并且可以在应用程序委托中引用为 self.gameData

假设 #2 - PrimaryViewController,正如您所命名的,是故事板中的主视图控制器。

假设 #3 - 您使用 NSTimer 每 1 秒更新一次此计数。

假设 #4 - 您的 Storyboard 文件名为 Mainstorybaord

RWGameData

在你的类定义之前添加到你的RWGameData.h

@class RWGameData;
@protocol RWGameStateProtocol <NSObject>
-(void)StateUpdateForGameData:(RWGameData*)data;
@end

RWGameData.h 中,在您的类定义中添加一个委托。

@interface RWGameData : NSObject
@property(weak)id<RWGameStateProtocol> delegate;
@end

在您的选择器timerCalled 中添加对您的委托的调用,通知它数据已更改。

-(void)timerCalled
{
            [RWGameData sharedGameData].regularBubbleCount++;
            /* THE ISSUE IS HERE */
            _regularBubLabel.text = [NSString stringWithFormat:@"%li", [RWGameData sharedGameData].regularBubbleCount];
            [[RWGameData sharedGameData] save];

    } NSLog(@"Regular Bubble Count: %li", [RWGameData sharedGameData].regularBubbleCount);

   /* Inform the delegate */
[self.delegate StateUpdateForGameData:self];
}

PrimaryViewController

一旦你定义了你的协议,让你的PrimaryViewcontroller 符合这个协议,类似于以下内容。

-(void)StateUpateforGameData:(RWGameData*)data
{
   self.regularBubLabel.text = [[NSString alloc]initWithFormat:@"%i",data.regularBubbleCount];
}

应用代理

剩下的就是在您的RWGameData 实例上设置委托。您可以通过要求故事板为您提供主视图控制器的实例来做到这一点。如果您使用情节提要,则默认情况下此方法为空。但是,由于我们要在显示主视图控制器之前对其进行修改,因此我们需要进行一些更改并执行 Storyboard 已经为您完成的一些工作。

在 ApplicationDelegate 的头文件中,为 UIWindowRWGameData 创建一个属性。

@interface YourAppDelegate : UIResponder <UIApplicationDelegate>
@property(strong,nonatomic) UIWindow *window;
@property(strong,nonatomic) RWGameData *gameData;
@end

现在我们已经设置了您需要执行一些额外工作的属性。请注意,通常您在使用情节提要时不必执行此步骤。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{
    /// Screen Information
    CGFloat scale = [[UIScreen mainScreen]scale];
    CGRect frame  = [[UIScreen mainScreen]bounds];

    UIWindow* window = [[UIWindow alloc]initWithFrame:frame];
    self.window = window;
    self.window.contentScaleFactor = scale;


    UIStoryboard *sb = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
    PrimaryViewController *vc = (PrimaryViewController*)[sb instantiateInitialViewController];

    RWGameData *gameData = [[RWGameData alloc]init];
    gameData.delegate = vc;
    self.gameData = gameData;

    [window setRootViewController:vc];
    [window makeKeyAndVisible];
    return YES;
}

这种方法不需要您将标签和游戏数据紧密结合。相反,您的游戏数据的任何更新现在都可以通知您的视图,并允许您的视图选择需要更新的标签。

【讨论】:

  • 非常感谢您的发帖。我已经到了这一部分:“现在您可能有一个用于计时器的选择器,它增加了 regularBubbleCount 的计数,您应该向您的委托添加一个调用,通知它数据已更改。”我使用的选择器是'timerCalled'。如何向我的委托添加调用以通知其数据已更改?
  • @AdamHPU 恰恰相反。您希望您的 RWGameData 通知您的委托数据已更改。在示例中,您的委托对象实际上是您的 PrimaryViewController(因为它符合新创建的协议)。我将使用您的实现来更新我的答案。
  • 我在尝试执行“应用程序委托”部分时收到警告。警告是:从不兼容的类型“PrimaryViewController *__strong”分配给“id”。我正在使用 [RWGameData sharedGameData].delegate = vc;和 _window.rootViewController = vc;
  • @AdamHPU 我充实了应用程序委托的实现。请注意,如果您收到保留周期警告,那么您必须将委托的属性设置为强与弱。此外,听起来你还没有更新你的PrimaryViewController` to conform to your protocol RWGameStateProtocol```
  • 我已更新我的代码以反映您建议的一些更改。我在头文件中的 PrimaryViewController 接口声明现在读取; @interface PrimaryViewController : UIViewController 和我收到的关于 id 问题的警告现已解决。我还不得不将故事板的名称更改为“Main”,因为文件名是“Main.storyboard”,并且由于某种原因它不会引用“Main.storyboard”。不过标签似乎没有更新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多