无法从子类更新 UI

Posted

技术标签:

【中文标题】无法从子类更新 UI【英文标题】:Can't Update UI from Subclass 【发布时间】:2013-12-30 19:44:34 【问题描述】:

我有一个子类UIViewController。我上面有 24 张扑克牌,一开始都是面朝下的。当我点击每张卡片时,它们需要“翻转”,这实际上意味着我正在将它们的背景图像从黑色更改为白色。但是,当我单击卡片时,我无法在 UI 中更改背景图像。是的,我确定(因为我在另一个班级做过)图像存在并且具有正确的名称。

我可以从 NSLog 调用中看到我的代码正在执行,但我对为什么我的 UI 不会更新感到困惑。谁能看到我做错了什么?


超级类

CardGameViewController.h

#import <UIKit/UIKit.h>
#import "Deck.h"
#import "CardMatchingGame.h"

@interface CardGameViewController : UIViewController

// protected (for subclassing)
- (Deck *) createDeck; // abstract

- (void) updateUI;
- (NSString *) titleForCard:(Card *) card;
- (UIImage *) backgroundImageForCard:(Card *) card;
- (NSMutableArray *) cardsSelected;
- (void) setCardsSelected:(NSMutableArray *)cardsSelected;
- (UILabel *) recentCardLabel;
- (void) setRecentCardLabel:(UILabel *)label;
- (NSInteger) gain;
- (UILabel *) matchAttemptLabel;
- (CardMatchingGame *) game;

@end

CardGameViewController.m

#import "CardGameViewController.h"

@interface CardGameViewController ()
@property (weak, nonatomic) IBOutlet UILabel *scoreLabel;
@property (strong, nonatomic) CardMatchingGame *game;
@property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons;
@property (strong, nonatomic) NSMutableArray *cardsSelected;
@property (weak, nonatomic) IBOutlet UILabel *recentCardLabel;
@property (weak, nonatomic) IBOutlet UILabel *matchAttemptLabel;
@property (nonatomic) NSInteger gain;
@end

@implementation CardGameViewController

- (CardMatchingGame *) game

    if (!_game) 
        _game = [[CardMatchingGame alloc]
                 initWithCardCount:[self.cardButtons count]
                         usingDeck:[self createDeck]];
    

    return _game;


- (Deck *) createDeck  // abstract

    return nil;


- (IBAction)touchCardButton:(UIButton *)sender

    self.gain = -self.game.score; // capture previous score

    NSUInteger chosenButtonIndex = [self.cardButtons indexOfObject:sender];

    [self.game chooseCardAtIndex:chosenButtonIndex]; // note that this will update the game score

    self.gain += self.game.score; // increment gain by updated score
    self.gain += [self.game getGuessPenalty]; // add back the penalty for guessing

    [self updateUI];


- (void) updateUI

    int i=1;
    for (UIButton *cardButton in self.cardButtons) 
        NSLog(@"%d", i++);
        NSUInteger cardButtonIndex = [self.cardButtons indexOfObject:cardButton];
        Card *card = [self.game cardAtIndex:cardButtonIndex];
        [cardButton setTitle:[self titleForCard:card] forState:UIControlStateNormal];
        [cardButton setBackgroundImage:[self backgroundImageForCard:card] forState:UIControlStateNormal];
        cardButton.enabled = !card.isMatched;
    

    self.scoreLabel.text = [NSString stringWithFormat:@"Score: %d", self.game.score];    


- (NSString *) titleForCard:(Card *)card 
    return card.isChosen ? card.contents : @"";


- (UIImage *) backgroundImageForCard:(Card *) card 
    return [UIImage imageNamed:card.isChosen ? @"cardfront" : @"cardback"];


@end

子类

SetGameViewController.h

#import "CardGameViewController.h"

@interface SetGameViewController : CardGameViewController

@end

SetGameViewController.m

#import "SetGameViewController.h"
#import "SetCardDeck.h"

@interface SetGameViewController ()
@end

@implementation SetGameViewController

- (void) viewDidLoad

    [super viewDidLoad];
    [self updateUI];


- (Deck *) createDeck

    return [[SetCardDeck alloc] init];


- (void) updateUI

    [super updateUI];

    self.recentCardLabel.text = [NSString stringWithFormat:@"Recent Cards:"];


- (NSString *)titleForCard:(Card *)card


    NSString *string;

    string = @"A";

    return string;



- (UIImage *) backgroundImageForCard:(Card *)card

    UIImage *image = [UIImage imageNamed:@"cardback"];
    if (!image) NSLog(@"no such image");
    else NSLog(@"image found");
    return image;

@end

我的两个 NSLog 命令的输出如下所示:

1
image found
2
image found
...
24
image found

所以我知道我的代码正在以正确的顺序执行,子类层次结构是正确的,等等。我什至可以在我的 UI 上获取标签文本以进行更新。但我就是无法改变这些卡片(UIButtons)上的背景图像。我做错了什么?

谢谢。对不起,很长的帖子。这有点像大海捞针,我找不到针。

【问题讨论】:

你在哪里更改card.isChosen 标志? 这发生在另一个班级。即使我取出三元运算符并强制return [UIImage imageNamed:@"cardfront",它仍然不起作用。 确保从主线程调用 updateUI。设置断点并检查调用堆栈或添加断言,NSAssert([NSThread isMainThread], @"Not on the main thread"); 我添加了 NSAssert,它似乎在主线程上。还用断点验证了这一点并查看了调用堆栈。还是一头雾水。感谢您的建议。 【参考方案1】:

只是一个疯狂的猜测,你的子类不应该调用[super backgroundImageForCard] 吗?

【讨论】:

没有。通过在子类中声明该方法,我将覆盖超类方法。我知道调用超类方法的唯一原因是在执行特定于子类的代码之前是否有要执行的代码。就我而言,我的子类方法完全覆盖了超类方法。虽然在这种情况下,代码本质上是多余的,所以我现在什至不需要子类方法(我会在构建更多的时候,这就是我在那里拥有它的原因)。 但是你永远不会根据 isChosen 属性得到正确的图像。如果我正确阅读了您的问题,这不是问题所在吗? 见我上面的评论。即使我将方法更改为只说return [UIImage imageName:@"cardfront",它仍然不起作用。其实我可以把整个超类方法注释掉,还是一样的问题。如您所见,我知道我的子类方法是基于 NSLog 输出运行的方法。

以上是关于无法从子类更新 UI的主要内容,如果未能解决你的问题,请参考以下文章

无法在 C# 中更新 UI 组件

Swift 4:设备旋转后 UI 无法更新

UI 测试失败:无法更新到请求的方向

无法使用颤振 Bloc 模式更新小部件外部的 UI

如果在 ItemView 中更新,则无法刷新 UI

无法在 ViewController 和子类之间传递数据