【问题标题】:UIView hidden is not getting setUIView hidden 未设置
【发布时间】:2021-12-03 06:39:35
【问题描述】:

我有一个 UIView 子类,它作为 UIStackView 的排列子视图添加。根据模型中的数据,我想隐藏或显示排列的子视图(称为myView),但问题是当我去隐藏它时,即使我设置了myView.hidden = NO,它仍然显示@ 987654324@.

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

我已设置断点并单步执行此代码并使用 LLDB 在执行第 4 行之前验证 self.myView.hidden == YES。然后我在跨过第 4 行后立即检查了该值,它仍然是 YES。但是第 4 行明确地将其设置为 NO 并且 myView 的实现中没有任何内容覆盖甚至设置或检查其自身的 hidden 属性。因此,在此设置隐藏只是转到标准 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。

设置前隐藏=NO: >

设置hidden=NO后:>

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

不应该在这里 - 在再次设置 hidden=NO 之前:>

不应该在这里 - 再次设置 hidden=NO 后:>

终于不再隐藏:>


更新 2

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

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

调试会话

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

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

(lldb) p self.myView.hidden
(BOOL) $12 = 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) $13 = NO

(lldb) p self.myView.hidden
(BOOL) $15 = 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) $16 = NO

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

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

MyView.h

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

@interface MyView : UIView

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

@end

我的视图.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

请让我知道我是否应该发布任何其他可以提供帮助的内容,或者是否有任何我可以尝试的内容。我可以在我的代码中写两次值,但是不明白为什么我必须这样做,我觉得这有点危险,因为我怎么知道两次总是足够的?另外,必须连续两次将一个变量设置为相同的值才能正常工作,这很奇怪。

感谢大家为此提供的帮助。

【问题讨论】:

  • 这当然很奇怪。但是,您做错了一件事:不要以这种方式使用po。只需使用p。所以p self.myView.hidden。也不要在self.myView.hiddenself.myView.isHidden 之间来回摆动。你在滥用isHidden;它实际上不是属性的名称,它是 getter 方法。所以说[self.myView isHidden]self.myView.hidden。我并不是说这些都可以解决问题,我只是建议您作为一种良好做法表现得更正确一些。
  • 这里肯定发生了其他事情。我把你的代码添加到一个连接到 IBOutlet UIStackView 的空白 Obj-C 项目 (pastebin.com/zgZCHRxz) 中,并将 bool 更改为 YES/NO 分别按预期工作。
  • 是的,我同意这一点。我们无法重现该问题,因此您的代码中的 something else 正在潜入并以某种方式搞砸了。
  • @matt 感谢您的解释。我已经更新了代码,所以我只在设置属性时使用myView.hidden,在阅读它时,我使用[myView isHidden]。我还将我的 LLDB 语句更新为 p self.myView.hidden。我只是不确定有什么问题了。我明确地将属性设置为NO,但它仍然是YES,除非我设置了两次。我用一些新的日志语句发布了对该问题的更新,我刚刚运行了它,它显示了有关所涉及对象的更多详细信息。但这仍然很奇怪。我只是设置一个属性。再次感谢。
  • @brandonscript 感谢您的帮助。是的,我不认为它会很容易重现。如果有帮助,我会在问题底部发布更新,其中包含一些额外的日志记录。但基本上我将 hidden 属性设置为 NO,然后我必须再次设置它才能使更改生效。我不知道为什么。同样,我没有覆盖任何 setter 或 getter,也没有在其他任何地方设置 hidden 属性。我只是不确定可能出了什么问题,甚至不确定如何进行调试。再次感谢您的帮助。

标签: ios objective-c uiview lldb uistackview


【解决方案1】:

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

  • UIStackView
    • 我的视图

那么,如果你设置MyViewhidden=YES 3 次,则需要设置3 次hidden=NO 才能真正设置为NO。这似乎不是另一个问题。所以如果你设置hidden=NO 3次,你可以设置hidden=YES一次,它就会被隐藏。

此 StackOverflow 答案中有更多信息: https://stackoverflow.com/a/45599835/5140550

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

【讨论】:

  • 你也在制作动画吗?
  • @DonMag 是的,它确实有动画效果。我有这个设置器,它可以加载或不加载动画的新数据。当页面首先加载此部分时,它不会动画,但选择收集视图项目并重新加载页面时,它以动画的方式确实如此。但我认为问题只是在设置堆栈视图中的视图的隐藏属性时, hidden=YES 写入会累积。因此,如果您设置 hidden=YES 七次,那么您需要多次将其设置为 NO 才能覆盖它。这已在此处报告:openradar.me/25087688
  • 好的——这将是非常有用的信息。而且,创建一个可重现的示例非常简单......
【解决方案2】:

是的,在为 UIStackView 中排列的子视图的显示/隐藏设置动画时存在错误/怪癖。

您应该能够通过将其添加到您的自定义视图类来更正该问题:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-30
    相关资源
    最近更新 更多