【问题标题】:Programmatically changing a UILabel from the App Controller in a Navigation Based iOS App在基于导航的 iOS 应用程序中以编程方式从应用程序控制器更改 UILabel
【发布时间】:2011-05-06 04:27:39
【问题描述】:

我在看似非常简单的事情上遇到了很多麻烦。我无法从基于导航的 iOS 应用程序以编程方式更新 UILabel。我不想使用按钮,因为此标签旨在报告外部系统的状态,并且应该在启动时更新。如果我不需要,则无需让用户完成触摸按钮的额外步骤。

以下是我已采取的步骤的详尽列表。如果其中一些看起来不必要,我很抱歉,但根据我的经验,即使是最小的被遗忘的步骤也可能是问题的原因。

从 Xcode 中一个新的基于导航的应用程序中,我正在采取以下步骤:

  1. 将 UITableView 替换为通用 UIView 类
  2. 将文件所有者的视图出口重新连接到新的 UIView
  3. 在 UIView 的中心添加一个 UILabel,使文本居中,并保留默认文本。
  4. 保存并退出界面生成器
  5. RootViewController.h <pre>#import &lt;UIKit&gt; @interface RootViewController : UIViewController { UILabel *myLabel; } @property (nonatomic, retain) IBOutlet UILabel *myLabel; @end
  6. RootViewController.m
    #import "RootViewController.h"
    @implementation RootViewController
    @synthesize myLabel;
    ...
  7. 从 RootViewController.m 中删除了 TableView 内容
  8. 将 IBOutlet myLabel 连接到 RootViewController.xib 中的标签
  9. 保存并退出界面生成器
  10. tempNavAppAppDelegate.m
    ...
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
    // Add the navigation controller's view to the window and display. [self.window addSubview:navigationController.view]; [self.window makeKeyAndVisible];
    RootViewController *rootViewCont = navigationController.visibleViewController; rootViewCont.myLabel.text = @"test"; NSLog(@"Label Text: %@", rootViewCont.myLabel.text);
    return YES; } ...
  11. 构建/运行

标签显示为“标签”而不是“测试”。并且日志报告:tempNavApp[94186:207] Label Text: (null)

我已经尝试了许多不同的方法来完成这项工作,但我们将不胜感激。

【问题讨论】:

    标签: objective-c cocoa-touch ios


    【解决方案1】:

    旅程

    在发现我的rootViewCont.myLabel 也是nil 后,感谢mprudhom 的帮助,我决定测试一下是否可以在RootViewController.m 的- (void)viewDidLoad 方法中为myLabel.text 赋值。 它起作用了,我能够直接从 RootViewController 更改文本。但是,虽然这证明了我的 View Controller 没有损坏,但它并没有解决我最初希望从 tempNavAppAppDelegate.m 更改 UILabel 的愿望。

    Elliot H. 然后建议navigationController.visibleViewController 实际上并没有返回视图控制器。我已经测试了rootViewCont 的值,它以 RootViewController 的形式返回,但 Elliot 的建议让我开始思考应用程序的生命周期以及我的代码的不同部分何时实际加载。

    所以我开始在启动过程的每一步打印一个 NSLog(application:didFinishLaunchingWithOptions:, applicationDidBecomeActive:, viewDidLoad, viewDidAppear:),我惊讶地发现[self.window makeKeyAndVisible]; 并不意味着视图会在应用程序之前加载:didFinishLaunchingWithOptions: 完成。

    有了这些知识,我知道问题出在哪里。解决方案(或者至少是我的解决方案)似乎是 NSNotificationCenter。我现在已经在 tempNavAppAppDelegate 中注册了通知,并且正在 RootViewController 的 viewDidAppear: 方法中广播通知。


    相关代码

    RootViewController.h:

    @interface RootViewController : UIViewController {
        IBOutlet UILabel *myLabel;
    }
    @property (nonatomic, retain) UILabel *myLabel;
    @end

    RootViewController.m:

    @implementation RootViewController
    @synthesize myLabel;
    - (void)viewDidLoad {
        [super viewDidLoad];
        NSParameterAssert(self.myLabel);
    }
    - (void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"viewDidAppear" object:self];
    }
    

    tempNavAppAppDelegate.h:

    @interface tempNavAppAppDelegate : NSObject  {
        UIWindow *window;
        UINavigationController *navigationController;
    }
    @property (nonatomic, retain) IBOutlet UIWindow *window;
    @property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
    - (void)viewDidAppearNotification:(id)notification;
    @end
    

    tempNavAppAppDelegate.m:

    @implementation tempNavAppAppDelegate
    @synthesize window;
    @synthesize navigationController;
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        [self.window addSubview:navigationController.view];
        [self.window makeKeyAndVisible];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidAppearNotification:) name:@"viewDidAppear" object:nil];
        return YES;
    }
    - (void)viewDidAppearNotification:(id)notification
    {
        NSString *noteClass = [NSString stringWithFormat:@"%@", [[notification object] class]];
        if ([noteClass isEqualToString:@"RootViewController"]) {
            RootViewController *noteObject = [notification object];
            noteObject.myLabel.text = @"Success!";
        }
    }
    

    【讨论】:

    • 感谢大家的帮助。每个响应都帮助我跟踪问题,如果没有您的帮助,我不可能得出上述解决方案。
    【解决方案2】:

    如果此代码打印 nil:

    rootViewCont.myLabel.text = @"test";
    NSLog(@"Label Text: %@", rootViewCont.myLabel.text);
    

    那么几乎可以肯定是因为 rootViewCont.myLabel 本身是 nil。尝试记录 rootViewCont.myLabel 的值,你会看到。

    您确定将标签连接到 Interface Builder 中的 UILabel IBOutput 声明吗?这是最常见的问题。

    我个人总是在 viewDidLoad 中声明所有预期的出口,以便我尽早发现出口在 Interface Builder 中(是否意外)被解耦。例如:

    - (void)viewDidLoad {
        [super viewDidLoad];
        NSParameterAssert(rootViewCont.myLabel);
    }
    

    【讨论】:

    • NSParameterAssert 的巧妙技巧,感谢您的建议。我把它放在 RootViewController.m 中作为NSParameterAssert(self.myLabel); 它没有抛出任何错误,所以假设我做对了,我不认为这是我的问题。
    【解决方案3】:

    你的界面应该是这样的

    #import <UIKit>
    @interface RootViewController : UIViewController {
        // IBOutlet here...
        IBOutlet UILabel *myLabel;
    }
    @property (nonatomic, retain) UILabel *myLabel;
    @end
    

    【讨论】:

    • 我不确定我是否理解这将如何影响应用程序,因为编译器忽略了 IBOutlet 并且 Interface Builder 允许我建立连接。尽管如此,我还是做了您建议的更改并重新连接了 Interface Builder 中的标签。
    【解决方案4】:

    visibleViewController 真的返回视图控制器吗?我的猜测是由于 application:didFinishLaunchingWithOptions: 尚未返回,可能 UINavigationController 尚未正确配置该属性以返回,即使您已将导航控制器的子视图添加到视图层次结构中,也可能不是 visibleViewController在相关视图控制器上调用 viewDidAppear: 之前有效。

    尝试直接为 RootViewController 设置一个 IBOutlet,或者以编程方式创建它,然后分配标签文本。

    一般提醒:如果一个对象是 nil(在这种情况下 visibleViewController 将返回 nil),并且您向它发送消息,您不会崩溃,因为发送到 nil 的消息是有效的并且不会做任何事情。当您在 rootViewCont 对象上调用 myLabel 访问器时,如果 rootViewCont 为 nil,myLabel 将始终返回 nil。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多