未设置隐藏的 UIView

UIView hidden is not getting set

我有一个 UIView subclass 添加为 UIStackView 的 ar运行ged 子视图。根据模型中的数据,我想隐藏或显示 ar运行ged 子视图(称为 myView),但问题是当我去隐藏它时,即使我设置 myView.hidden = NO,还是显示myView.hidden = YES.

例如,下面是我的代码。它从隐藏视图开始,根据是否设置了 myModel.someProperty,它将显示 myView。或者这就是应该发生的事情。

我已经设置了一个断点并单步执行了这段代码,并在第 4 行执行之前使用 LLDB 验证 self.myView.hidden == YES。然后我在跨过第 4 行后立即检查了该值,它仍然是 YES。但是第 4 行明确地将其设置为 NO,并且在 myView 的实现中没有任何内容覆盖甚至设置或检查其自身的隐藏 属性。所以设置隐藏只是进入标准的 UIView setHidden: 方法。那怎么可能还是YES呢?


1.   //currently, self.myView.hidden is YES
2.   
3.   if (self->_myModel.someProperty) {
4.     self.myView.hidden = NO;
5.           
6.     //for some reason, self.myView.hidden is still YES
7.   
8.     while (self.myView.isHidden) {
9.       NSLog(@"myView is hidden, but it should not be");
10.      self.myView.hidden = NO;
11.    }
12.    NSLog(@"myView is no longer hidden");
13.  }

我在第 8 行添加了一个循环,它将导致视图再次被隐藏。这次成功了。所以如果我设置 myView.hidden = NO 两次,那么它实际上会被设置为 NO。但如果我只设置一次,那么它会保持在 YES。我不明白这是怎么回事。

有谁知道这里可能出了什么问题或如何进一步解决这个问题?我用LLDB的po命令查看了每组属性前后myView.isHidden的值。所以在第 4 行之前,它被设置为 YES,这是正确的。然后,在第 4 行之后,我检查了它,它仍然设置为 YES,即使它在上一行中明确设置为 NO。然后,我检查了一下,它进入了第 8 行的循环(即使它不应该像它应该的那样被隐藏)。然后我在第 10 行之前再次检查,myView.hidden 仍然是 YES,我在第 10 行之后检查,它最终正确设置为 NO。

但我只是不确定发生了什么。这是非常违反直觉的,因为我明确地将其设置为 NO,但直到我将其设置两次为 NO 后它才被设置。

是否有解决此问题或找出问题所在的好方法,或者有人对可能出现的问题有任何建议吗?


更新

我更新了代码以添加一些额外的日志语句。在 LLDB 中检查 属性 时,我也使用了 p self.myView.hidden

1.   // at this point, self.myView.hidden = YES
2.   
3.   if (self->_myModel.someProperty) {
4.     NSLog(@"Before setting hidden=NO: %@", self->_myView);
5.     self.myView.hidden = NO;
6.     NSLog(@"After setting hidden=NO: %@", self->_myView);
7.     
8.     while ([self.myView isHidden]) {
9.       NSLog(@"SHOULD NOT BE HERE - Before setting hidden=NO again: %@", self->_myView);
10.       self.myView.hidden = NO;
11.       NSLog(@"SHOULD NOT BE HERE - After setting hidden=NO again: %@", self->_myView);
12.     }
13.     
14.     NSLog(@"Finally, no longer hidden: %@", self->_myView);
15.   }

这是这段代码的日志语句。第一个日志语句是正确的,因为它显示 myView.hidden == YES。然而,第二个日志语句对我来说似乎是错误的,因为它仍然显示 myView.hidden == YES 即使在前一行它只是设置为 NO.

Before setting hidden=NO: <MyView: 0x117ef6eb0; frame = (0 49.6667; 123.667 20.3333); hidden = YES; layer = <CALayer: 0x280ddaa20>>

After setting hidden=NO: <MyView: 0x117ef6eb0; frame = (0 49.6667; 123.667 20.3333); hidden = YES; layer = <CALayer: 0x280ddaa20>>

下一组日志语句在循环内,它甚至不应该进入循环,因为我将 myView.hidden 设置为 NO,但无论如何它都会进入,因为值仍然是 YES。在这里它看起来工作正常。第一个日志语句显示它是可见的,然后下一个日志语句显示它是隐藏的。

SHOULD NOT BE HERE - Before setting hidden=NO again: <MyView: 0x117ef6eb0; frame = (0 49.6667; 123.667 20.3333); hidden = YES; layer = <CALayer: 0x280ddaa20>>

SHOULD NOT BE HERE - After setting hidden=NO again: <MyView: 0x117ef6eb0; frame = (0 49.6667; 123.667 20.3333); layer = <CALayer: 0x280ddaa20>>

Finally, no longer hidden: <MyView: 0x117ef6eb0; frame = (0 49.6667; 123.667 20.3333); layer = <CALayer: 0x280ddaa20>>


更新 2

我知道这段代码似乎可以独立运行,但在我的项目中对我不起作用。我将在此处显示我的视图 class 的代码以及调试会话的输出,显示在代码中观察到的相同行为。

而且我知道它可能在我的代码中,但与此同时,我只是不知道如何实现。我的所有代码都包含这里是对 setHidden: 的调用。没什么额外的。在调用setHidden之前,hidden的值为YES。调用setHidden:NO后,值还是YES。我不明白这个。我想知道这是否可能是编译器问题。我知道这些编译器都经过了很好的测试,但同时我也不明白我的代码是怎么来的。我只是简单地设置了 hidden = NO,但它不起作用,除非我设置两次。

调试会话

这是 LLDB 的输出。我在视图即将取消隐藏之前设置了一个断点(前面代码片段中的第 3 行)。此时,myView.hidden = YES.

所以我所做的就是打印那个视图的 hidden 值,它正确地显示了 YES。在此之后,我 运行 call self.myView.hidden = NO 尝试更新它,但这不起作用,正如在调用语句下方打印的调试语句中所见。它仍然显示 hidden = YES;。我也继续并再次打印该值只是为了确定,它仍然显示 hidden = YES.

(lldb) p self.myView.hidden
(BOOL)  = YES

(lldb) call self.myView.hidden = NO
<MyView: 0x12b138980; frame = (0 49.6667; 123.667 20.3333); hidden = YES; layer = <CALayer: 0x283addfe0>> MyView::setHidden:NO
(BOOL)  = NO

(lldb) p self.myView.hidden
(BOOL)  = YES

接下来,我只是再次将该值设置为 NO,这一次它的工作原理可以从调试语句中看出,我还再次打印了该值以备不时之需。

(lldb) call self.myView.hidden = NO
<MyView: 0x12b138980; frame = (0 49.6667; 123.667 20.3333); layer = <CALayer: 0x283addfe0>> MyView::setHidden:NO
(BOOL)  = NO

(lldb) p self.myView.hidden
(BOOL)  = NO

这是显示和隐藏我的视图 class 的代码。我没有覆盖或对隐藏的 属性 做任何事情,所以对 setHidden: 的任何调用都会直接转到 UIView 上的方法。

MyView.h

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

@interface MyView : UIView

@property (strong, nonatomic, nullable) MyModel *myModel;

@end

MyView.m

#import "MyView.h"

@interface MyView ()

@property (strong, nonatomic) UILabel *label;
//other UI components are here, but they are just more labels and an image view

@end

@implementation MyView

- (instancetype)init {
    return [self initWithFrame:CGRectZero];
}

- (instancetype)initWithCoder:(NSCoder *)coder {
    if (self = [super initWithCoder:coder]) {
        [self initialize];
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self initialize];
    }
    return self;
}

- (void)initialize {
    [self addSubview:self.label];
    //add other labels and the image view
    
    [NSLayoutConstraint activateConstraints:@[
        [self.label.leadingAnchor constraintGreaterThanOrEqualToAnchor:self.leadingAnchor],
        [self.label.topAnchor constraintGreaterThanOrEqualToAnchor:self.topAnchor],
        [self.label.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],

        //more constraints for the other labels and the image
    ]];
}

- (void)setMyModel:(MyModel *)myModel {
    self->_myModel = myModel;
    [self updateDisplay];
}

- (void)updateDisplay {
    //set the text of all the labels based on the model
}

- (UILabel *)label {
    if (!self->_label) {
        self->_label = [[UILabel alloc] init];
        self->_label.translatesAutoresizingMaskIntoConstraints = NO;
        self->_label.numberOfLines = 0;
        self->_label.text = @"My Text:";
        [self->_label setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
        [self->_label setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
    }
    return self->_label;
}

@end

请让我知道是否有任何其他我应该 post 会有所帮助的事情,或者是否有任何我可以尝试的事情。我可以在我的代码中只写两次值,但不理解为什么我必须这样做,我觉得这有点危险,因为我怎么知道两次就足够了?另外,必须连续两次将变量设置为相同的值才能工作,这很奇怪。

感谢大家对此的帮助。

这看起来是由于 UIStackView 中的一个错误,如果您多次隐藏一个视图,它会累积该视图的隐藏次数。因此,例如,想象一个像这样的视图层次结构:

  • UIStackView
    • 我的视图

那么,如果你设置了3次MyView hidden=YES,那么要设置3次hidden=NO才能真正设置为NO。这似乎不是另一个问题。所以设置3次hidden=NO,只设置一次hidden=YES就隐藏了。

这个 Whosebug 答案中有更多信息:

我不确定是否已将此错误报告给 Apple,但它似乎是 UIStackView 中的错误。现在我只需要找出一种干净的方法来在我的代码中处理这个问题。

是的,在 UIStackView.

中动画显示/隐藏排列的子视图时存在错误/怪癖

您应该可以通过将此添加到您的自定义视图来更正此问题 class:

- (void)setHidden:(BOOL)hidden {
    if (self.isHidden != hidden) {
        [super setHidden:hidden];
    }
}

这是一个完整的示例,显示了问题并显示了“修复”:https://github.com/DonMag/StackViewBug