未设置 UIView 隐藏

UIView hidden is not getting set

提问人:ashipma 提问时间:10/15/2021 最后编辑:ashipma 更新时间:10/16/2021 访问量:923

问:

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

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

我已经设置了一个断点并单步执行此代码,并在执行第 4 行之前使用 LLDB 进行验证。然后,我在跨过第 4 行后立即检查了该值,它仍然是 YES。但是第 4 行显式将其设置为 NO,并且 myView 实现中的任何内容都不会覆盖甚至设置或检查自身的隐藏属性。因此,在此上设置隐藏只会转到标准的 UIView 方法。那么它怎么可能仍然是肯定的呢?self.myView.hidden == YESsetHidden:


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 行添加了一个循环,这将导致视图再次隐藏。这次有效。因此,如果我设置两次,那么它实际上将设置为 NO。但是,如果我只设置一次,那么它就会保持在 YES。我不明白这是怎么回事。myView.hidden = NO

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

但我只是不确定发生了什么。这是非常违反直觉的,因为我明确将其设置为 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。

设置前 hidden=NO: <MyView: 0x117ef6eb0;帧 = (0 49.6667; 123.667 20.3333);隐藏 = 是;layer = <CALayer:0x280ddaa20>>

设置后 hidden=NO: <MyView: 0x117ef6eb0;帧 = (0 49.6667; 123.667 20.3333);隐藏 = 是;layer = <CALayer:0x280ddaa20>>

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

SHOULD NOT BE HERE - 再次设置 hidden=NO 之前: <MyView: 0x117ef6eb0;帧 = (0 49.6667; 123.667 20.3333);隐藏 = 是;layer = <CALayer:0x280ddaa20>>

SHOULD NOT BE HERE - 再次设置 hidden=NO 后: <MyView: 0x117ef6eb0;帧 = (0 49.6667; 123.667 20.3333);layer = <CALayer:0x280ddaa20>>

最后,不再隐藏:<MyView:0x117ef6eb0;帧 = (0 49.6667; 123.667 20.3333);layer = <CALayer:0x280ddaa20>>


更新 2

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

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

调试会话

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

因此,我所做的只是为该视图打印隐藏的值,它正确地显示是。在此之后,我跑去尝试更新它,但这不起作用,正如在调用语句正下方打印出来的调试语句中所示。它仍然显示.为了确定,我还继续再次打印了该值,它仍然显示隐藏 = YES。call self.myView.hidden = NOhidden = 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

这是我的视图类的代码,它被显示和隐藏。我没有重写或对隐藏属性执行任何操作,因此任何调用都会直接转到 UIView 上的方法。setHidden:

我的视图.h

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

@interface MyView : UIView

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

@end

我的View.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

如果还有什么我应该发布的会有所帮助,或者有什么我可以尝试的,请告诉我。我可以在我的代码中只写两次值,但是在不理解为什么我必须这样做的情况下,我觉得这有点危险,因为我怎么知道两次总是足够的?另外,必须连续两次将变量设置为相同的值才能使其工作,这很奇怪。

感谢大家对此的帮助。

iOS Objective-C UIView LLDB UISkackView

评论

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

答:

0赞 ashipma 10/16/2021 #1

这似乎是由于 UIStackView 中的一个错误,如果您多次隐藏视图,它会累积该视图的隐藏计数。因此,例如,假设视图层次结构如下:

  • UIStack视图
    • 我的视图

然后,如果将 hidden=YES 设置为三次,则需要设置 hidden=NO 三次才能将其实际设置为 NO。反过来说,这似乎不是一个问题。因此,如果您将其 hidden=NO 设置为三次,您可以将其设置为 hidden=YES 一次,它就会被隐藏。MyView

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

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

评论

0赞 DonMag 10/16/2021
你也在为它制作动画吗?
0赞 ashipma 10/16/2021
@DonMag 是的,它确实被动画化了。我有 setter 用于此,它可以加载动画或不加载新数据。当页面首次加载此部分时,它不会进行动画处理,但是当选择集合视图项并重新加载页面时,它会以动画方式加载。但我认为问题只是在设置堆栈视图中视图的隐藏属性时,hidden=YES 写入将累积。因此,如果您将 hidden=YES 设置为 7 次,则需要将其设置为 NO 那么多次才能覆盖它。这是在这里报告的:openradar.me/25087688
1赞 DonMag 10/16/2021
好的 - 这将是非常有用的信息。而且,创建一个可重现的例子非常简单......
3赞 DonMag 10/16/2021 #2

是的,在对 .UIStackView

您应该能够通过将以下内容添加到自定义视图类来纠正此问题:

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

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

评论

0赞 Pankaj Kulkarni 7/26/2023
这在 iOS 16/Xcode 14.3.1 中仍然是一个问题。而且该解决方案仍然有效。这甚至适用于 UIView 的子类。