2

当在另一个类中调用方法时,我想更改另一个对象的属性。

更改此对象属性的代码位于第一个类的方法中,并且在从它自己的类调用它时起作用,但是当从另一个类调用时,方法中的对象返回 nil。

这是代码:

视图控制器.h

@interface ViewController : UIViewController {

    UIView *menuView; //the object

}

@property (nonatomic, retain) IBOutlet UIView *menuView;

-(void)closeMenu; //the method

@end

视图控制器.m

@implementation ViewController

@synthesize menuView;

-(void)closeMenu{

    [menuView setFrame:CGRectMake(menuView.frame.origin.x, -menuView.frame.size.height, menuView.frame.size.width, menuView.frame.size.height)];

    NSLog(@"%f", menuView.frame.size.height); //returns height when method is called from it's own class. But returns 0 (nil) when called from the other class.

}

SDNestedTableViewController.h(没什么重要的,但可能有帮助?)

@interface SDMenuViewController : SDNestedTableViewController{


}

SDNestedTableViewController.m

#import "SDMenuViewController.h"
#import "ViewController.h"

- (void) item:(SDGroupCell *)item subItemDidChange:(SDSelectableCell *)subItem
{

    ViewController *firstViewController = [[[ViewController alloc] init] autorelease];

    SelectableCellState state = subItem.selectableCellState;
    NSIndexPath *indexPath = [item.subTable indexPathForCell:subItem];
    switch (state) {
        case Checked:
            NSLog(@"Changed Sub Item at indexPath:%@ to state \"Checked\"", indexPath);


            [firstViewController closeMenu]; //called from other class


            break;
        case Unchecked:
            NSLog(@"Changed Sub Item at indexPath:%@ to state \"Unchecked\"", indexPath);
            break;
        default:
            break;
    }
}
4

11 回答 11

4

您发布的内容如下所示:

-(void)closeMenu{
    // menuView is never initialized, == nil
    [nil setFrame:CGRectMake(0, -0, 0, 0)];

    NSLog(@"%f", 0); //returns height when method is called from it's own class. But returns 0 (nil) when called from the other class.

}

所以你在做NSLog(@"%f", 0);

如果您确实通过访问view属性来加载视图,则 menuView 将由 IB 规则初始化。有关 viewController 视图加载/卸载的详细信息,请参阅参考文档

于 2012-10-23T00:21:05.547 回答
2

我想这可能会对你有所帮助。

在您的AppDelegate班级,您必须声明一个ViewController班级对象。使其成为YourAppDelegate类的属性。如下所示。(这将导入ViewController类并创建类的共享对象,YourAppDelegate以便您可以YourAppDelegate通过简单地导入来全局访问类的成员YourAppDelegate.h)。

#import "ViewController.h"

#define UIAppDelegate ((YourAppDelegate *)[UIApplication sharedApplication].delegate)

@interface YourAppDelegate : NSObject <UIApplicationDelegate> 
{
     ViewController  *objViewController;
}

@property (nonatomic, retain) ViewController  *objViewController;

@end

YourAppDelegate.m并在文件中合成属性。

@implementation YourAppDelegate

@synthesize objViewController;

@end

然后棘手的部分是,您必须在加载类时备份ViewController类中的类对象。YourAppDelegateViewController

为此,首先YourAppDelegate.hViewController.h类中和ViewController.m实现viewWillAppear:委托处导入 ,如下所示。

- (void)viewWillAppear:(BOOL)animated 
{
    [super viewWillAppear:animated];
    UIAppDelegate.objViewController = self;
}

然后在SDNestedTableViewController.m,

#import "SDMenuViewController.h"
#import "ViewController.h"

- (void) item:(SDGroupCell *)item subItemDidChange:(SDSelectableCell *)subItem
{

    ViewController *firstViewController = (ViewController *)UIAppDelegate.objViewController;

    if(firstViewController && [firstViewController isKindOfClass:[ViewController class]])
    {
            SelectableCellState state = subItem.selectableCellState;
            NSIndexPath *indexPath = [item.subTable indexPathForCell:subItem];
            switch (state) {
              case Checked:
                        NSLog(@"Changed Sub Item at indexPath:%@ to state \"Checked\"", indexPath);


                        [firstViewController closeMenu]; //called from other class


                        break;
              case Unchecked:
                        NSLog(@"Changed Sub Item at indexPath:%@ to state \"Unchecked\"", indexPath);
                        break;
              default:
                        break;
            }
    }
}

试试这个方法。我并不是说这是正确的方法,但是,这应该有效。很高兴这对您有帮助。

于 2012-10-29T08:18:53.827 回答
1

问题是:

- (void) item:(SDGroupCell *)item subItemDidChange:(SDSelectableCell *)subItem {

    ViewController *firstViewController = [[[ViewController alloc] init] autorelease];

    ...

    [firstViewController closeMenu];


}

当您closeMenu从那里调用时,它永远不会被初始化,因为没有足够的时间来初始化view视图控制器,此时也不会调用viewDidLoad您的方法。也不是从 nib 创建的,所以这就是它的原因。firstViewControllermenuViewnil

也许由于某种原因,可能会产生足够长的延迟menuView,但这不是你应该在 iOS 中做事的方式。

因此,如果您不想显示您的menuView,只需向您的 and 添加一些布尔值,firstViewController而不是closeMenu这样做:

firstViewController.shouldCloseMenu = YES;

然后在您的ViewControllerinviewDidLoad方法中执行以下操作:

if (self.shouldCloseMenu ) {
    [self closeMenu];
}

也许这不是最好的方法,但现在你知道它应该如何工作了。

于 2012-10-23T00:53:16.857 回答
1

我相信您的问题与您初始化 viewController 的方式有关。

代替

ViewController *firstViewController = [[[ViewController alloc] init] autorelease];

采用

ViewController *firstViewController = [[[ViewController alloc] initWithNibName:@"yourNibName" bundle:nil] autorelease];

我假设您有一个笔尖,因为您使用的是 IBOutlet。但我相信 IBOutlet 永远不会设置,因为您尚未加载 nib 文件。

还要仔细检查您与界面生成器的 IBOutlet 连接并使用“self.menuView”

于 2012-10-28T02:14:09.807 回答
1

编辑2:

好吧,你把你的代码发给了我,所以现在我不能再说我没有足够的信息来解决你的问题。

让我们来看看。

现在我看到您的 ViewController 是您的应用程序的 rootViewController,如下所示:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    // Override point for customization after application launch.
    self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController" bundle:nil] autorelease];
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    return YES;
}

好,现在 ViewController 与您的 SDNestedTableViewController 有什么关系?

你在 ViewController 的 viewDidLoad 中有这个:

- (void)viewDidLoad
{
    [super viewDidLoad];

    SDMenuViewController *mvc = [[[SDMenuViewController alloc] initWithNibName:@"SDNestedTableView" bundle:nil] autorelease];
    [self addChildViewController:mvc];
    [mvc didMoveToParentViewController:self];
    [menuView addSubview:mvc.view];

    // Some other stuff with gesture recognizers I'm omitting...

    [self openMenu];

}

好的,所以看起来 SDMenuViewController 是 ViewController 的孩子。现在,您在 SDMenuViewController 中有一个名为 item:subItemDidChange 的方法:

- (void) item:(SDGroupCell *)item subItemDidChange:(SDSelectableCell *)subItem
{

    ViewController *firstViewController = [[[ViewController alloc] initWithNibName:@"ViewController" bundle:nil] autorelease];

    SelectableCellState state = subItem.selectableCellState;
    NSIndexPath *indexPath = [item.subTable indexPathForCell:subItem];
    switch (state) {
        case Checked:
            NSLog(@"Changed Sub Item at indexPath:%@ to state \"Checked\"", indexPath);

            //close the menuView

            [firstViewController closeMenu];

            break;
        case Unchecked:
            NSLog(@"Changed Sub Item at indexPath:%@ to state \"Unchecked\"", indexPath);
            break;
        default:
            break;
    }
}

所以,你希望引用回到现有的ViewController 对象,对吧?因为在那里你正在制作另一个。所以,你可以这样做:

ViewController *firstViewController = self.parentViewController;

这让您可以参考 SDMenuViewController 的父级,即 ViewController 的实例。此属性在您addChildViewController:拨打电话时设置。


好的,虽然这很令人困惑:

在您的帖子中,您说您的item:subItemDidChange:方法在 SDNestedTableViewController 中,但在您发送给我的代码中,它在 SDMenuViewController 中。

在SDNestedTableViewController中,我找到了这个方法:

- (void) mainItem:(SDGroupCell *)item subItemDidChange: (SDSelectableCell *)subItem forTap:(BOOL)tapped
{
    if(delegate != nil && [delegate respondsToSelector:@selector(item:subItemDidChange:)] )
    {
        [delegate performSelector:@selector(item:subItemDidChange:) withObject:item withObject:subItem];
    }
}

所以看起来你没有使用与原始帖子中相同的代码,但足够接近,无论如何。


现在,如果您想从应用程序中的任何位置获取对 ViewController 实例的引用,而不仅仅是您的 SDMenuViewController(恰好是 ViewController 实例的子实例),您应该使用 @Mathew Varghese 的答案

这是此方法的重述:

  1. 将该行添加+ (AppDelegate *)instance;到您的 AppDelegate.h 文件中。
  2. 将以下方法添加到您的 AppDelegate.m 文件。

像这样:

+ (AppDelegate *)instance
{
    AppDelegate *dg = [UIApplication sharedApplication].delegate;
    return dg;
}

然后,在任何你想要引用的对象中,你#import AppDelegate.hViewController *vc = AppDelegate.instance.firstViewController;

无论如何,这只是马修前面提到的另一种说法。

于 2012-10-31T21:25:42.433 回答
0

我建议您通过以下步骤解决此问题。

  1. 不要firstViewControllerSDMenuViewController.

  2. 在案例检查块中,向NSNotificationCenter

  3. ViewController注册具有相同消息 Id 的消息时,使用该closeMenu方法作为其处理程序。

对我来说,使用消息中心来调度处理可以解耦控制器之间的关系。这是一种更好的方法,您可以较少关注另一个控制器的生命周期。

希望它会有所帮助。

于 2012-10-23T00:51:27.360 回答
0

alloc-init'ing 一个 ViewController 和alloc-init'ing 那个视图控制器的属性是有区别的。

关于你的第二个例子(从另一个班级打电话)。您当前的代码表明您alloc-init首先使用 ViewController,但随后不对其进行任何操作。假设您没有覆盖 ViewController 的init方法,它的属性和 iVar 应该是nil(或最坏的情况下未定义)。你需要alloc-init你的firstViewController.menuView第一个。IE:

firstViewController.menuView = [[UIView alloc] initWithFrame]; // Don't do this.

这种方法的问题是您正在从另一个类设置 firstViewController 的属性,这通常是相当平均的设计实践。这个必需的设置通常会发生在viewDidLoad但是因为你还没有对 firstViewController 做任何事情,所以它永远不会被调用。

相反,当您closeMenu从它自己的 View Controller 调用时,您实际上是在对视图执行某些操作,并且menuView = [[UIView alloc] init];首先调用 viewDidLoad(或找到的任何位置),从而初始化您的 menuView 对象。

您需要确保首先初始化 menuView 对象,然后再尝试对其进行任何操作,仅初始化包含它的 View Controller 是不够的。

于 2012-10-29T07:19:37.917 回答
0
#import "SDMenuViewController.h"
#import "ViewController.h"

- (void) item:(SDGroupCell *)item subItemDidChange:(SDSelectableCell *)subItem
{

// 我们为什么要在这里分配这个对象,如果它只是在 Checked 的情况下需要的话:

    ViewController *firstViewController = [[[ViewController alloc] init] autorelease];
    SelectableCellState state = subItem.selectableCellState;
    NSIndexPath *indexPath = [item.subTable indexPathForCell:subItem];
    switch (state) {
        case Checked:
            NSLog(@"Changed Sub Item at indexPath:%@ to state \"Checked\"", indexPath);


            [firstViewController closeMenu]; //called from other class


            break;
        case Unchecked:
            NSLog(@"Changed Sub Item at indexPath:%@ to state \"Unchecked\"", indexPath);
            break;
        default:
            break;
    }
}

将其更改为

#import "SDMenuViewController.h"
#import "ViewController.h"

- (void) item:(SDGroupCell *)item subItemDidChange:(SDSelectableCell *)subItem
{

// 我们为什么要在这里分配这个对象,如果它只是在 Checked 的情况下需要的话:

    SelectableCellState state = subItem.selectableCellState;
    NSIndexPath *indexPath = [item.subTable indexPathForCell:subItem];
    switch (state) {
        case Checked:
            NSLog(@"Changed Sub Item at indexPath:%@ to state \"Checked\"", indexPath);


            // here no need to put object in autorelease mode.
            ViewController *firstViewController = [[ViewController alloc] init];
            [firstViewController closeMenu]; //called from other class
            [firstViewController release];

            break;
        case Unchecked:
            NSLog(@"Changed Sub Item at indexPath:%@ to state \"Unchecked\"", indexPath);
            break;
        default:
            break;
    }
}
于 2012-10-30T04:51:27.677 回答
0

一切都正确,更改-(void)closeMenu方法,如...

-(void)closeMenu
{
    menuView=[[UIView alloc]initWithFrame:CGRectMake(50.0,50.0,200.0,200.0)]
    NSLog(@"%f", menuView.frame.size.height); //returns height when method is called from it's own class. But returns 0 (nil) when called from the other class.
}

试试这个,让我知道。

于 2012-10-30T06:57:44.123 回答
0

尝试删除UIView *menuView; //接口文件中的对象

@interface ViewController : UIViewController {

    // try to remove this line
    UIView *menuView; //the object

}

并更新此方法

-(void)closeMenu{

    [self.menuView setFrame:CGRectMake(self.menuView.frame.origin.x, -self.menuView.frame.size.height, self.menuView.frame.size.width, self.menuView.frame.size.height)];

    NSLog(@"%f", self.menuView.frame.size.height);

}
于 2012-10-31T02:02:54.037 回答
0

我建议你使用这个:

if(menuView) {
    [menuView setFrame:CGRectMake(menuView.frame.origin.x, -menuView.frame.size.height, menuView.frame.size.width, menuView.frame.size.height)];
} else {
    NSLog(@"menuView is nil");
}
于 2012-10-31T13:17:21.547 回答