【问题标题】:Updating UILabel Text resets storyboard?更新 UILabel Text 会重置情节提要?
【发布时间】:2015-05-29 07:08:00
【问题描述】:

我遇到了一个奇怪的场景,我自己无法弄清楚。

我开始了一个小测试场景,其中包含一个几乎空的 iOS 单视图应用程序和一个故事板。故事板包含一个标签和单个 UIView,10x10,仅用于展示问题。当然,他们分配并连接了适当的 IBOutlets:

@property (nonatomic, weak) IBOutlet UILabel *label;
@property (nonatomic, weak) IBOutlet UIView *dot;

在 (void)viewDidLoad 方法中,我启动了一个计时器并将其添加到运行循环中,如下所示:

- (void)viewDidLoad {
    [super viewDidLoad];

    NSTimer * timer = [NSTimer timerWithTimeInterval:0.5 target:self selector:@selector(doSomething) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

在 (void)doSomething 方法中,我执行以下操作:

-(void)doSomething {
    // Lets move the dot to the right a bit further
    _dot.center = CGPointMake(_dot.center.x+1, _dot.center.y);

    // About every ten steps, update the label with the current
    // X position, to better show the problem
    CGFloat moduloResult = (float)((int)_dot.center.x % (int)10.0);
    if(moduloResult <= 0)
        _label.text = [NSString stringWithFormat:@"%f", _dot.center.x+1];
}

所以基本上,我每次将小 10x10 点视图的中心更改 1,并且每 10 次更新标签以显示当前 X 位置。就此而言,我更新标签文本的内容甚至都无关紧要,重要的是它会发生变化。而且我只每 10 次更改一次文本以更好地显示这里的问题。

如果我运行该代码,点会按预期向右移动,但是当我更新标签文本时,点视图会重置并跳回原来的位置。所以更新我的标签文本会重置我在情节提要上的其他视图,这完全让我感到困惑。

有人可以重现甚至解释吗?

谢谢大家,提前:)

亚历克斯

【问题讨论】:

  • 您是否在情节提要中设置了一些 AutoLayout 约束?顺便说一句,喜欢这个(int)10.0
  • 我没有设置任何特定的 AutoLayout 约束,所以视图有默认设置。
  • 也许你有一些自动布局约束,你没有设置它的框架,对吧?
  • 很有可能。没错,我没有明确设置它的框架。我只是将它放在情节提要上,其余部分默认保留。我只是不知道视图的默认 AutoLayout 约束是否会导致这种行为。有人知道吗?
  • 你能把你的故事板作为截图贴在这里吗?

标签: ios objective-c iphone xcode uilabel


【解决方案1】:

我能够重现所描述的行为,这就是它发生的原因:

  1. 默认情况下,元素的位置由自动生成的 AutoLayout 约束确定
  2. 更改dot 的位置时,您只是在更新它的当前帧,而不是基础约束
  3. 更改UILabel 的文本可能会更改UILabel 内容的大小,因此需要重新计算其框架。这里根据约束重新计算整个布局,因此也重置了dot 的框架。

为了解决,我建议正确指定 AutoLayout 约束并更新它们而不是直接定位。

【讨论】:

    【解决方案2】:

    差不多就是这样,AutoLayout 约束总是将视图推回原来的位置,我猜这有点道理,这就是 AutoLayout 应该做的。

    所以,因为我希望我的小 10x10 点视图在屏幕上移动(出于练习目的,我更改了我的代码。我试图解释它,目前缺乏声誉使我无法发布适当的图像。

    我制作了两个自定义 AutoLayout 约束,告诉它始终保持视图距离父视图左侧 120 像素和距离父视图顶部 100 像素。我还为每个 10 的固定宽度和高度指定了约束。因为对于有效的约束,您需要 x 和 y 以及高度和宽度的组合(至少据我所知)。

                      -
                      |
                      | Constraint *n* from top
                      |
    
                      |------|
      Constraint      | view |
      *n* from left   | _dot |
    |---------------  |------|
    

    然后我为这两个约束创建了两个 IBOutlets:

    @property (weak, nonatomic) IBOutlet NSLayoutConstraint *c1X;
    @property (weak, nonatomic) IBOutlet NSLayoutConstraint *c1Y;
    

    我将我的 (void)doSomething 方法更改为:

    -(void)doSomething {
    
        // Lets move the dot to the right a bit
        // further by changing its Constraint
        _c1X.constant++;
    
        // About every ten steps, update the labe with the current
        // X position, to better show the problem
        CGFloat moduloResult = (float)((int)_dot.center.x % 10);
        if(moduloResult <= 0)
            _label.text = [NSString stringWithFormat:@"%f - %f - %f", _c1X.constant, _dot.center.x, _dot.center.y];
    }
    

    它起作用了,改变约束而不是实际视图现在将视图移动到屏幕上。如果这是最佳实践,我不知道。如果我不是为了练习目的而尝试它,遵循一个古老的教程,我一开始就不会想出这个。

    希望有一天这可能会有所帮助。欢迎任何 cmets 或改进:)

    亚历克斯

    【讨论】:

      【解决方案3】:

      这似乎是一个老问题,但它也困扰着我。 我很困惑的是,在视觉层次结构底部更改 UILabel 中的文本会使整个故事板重新布局。

      不仅如此,我还拥有一些 Android 开发背景,我从未在 Android 中使用(类似的)TextView 目睹过这种行为。您可以按照自己的意愿更改其中的文本——从不更新更高级别的 UI。

      但@Jakub Vano 的回答让我意识到一件事:

      Android 中的 TextView 一直是固定的宽度和高度,并且视图内文本的更新不会改变它的边框。 相反,iOS 的 UILabel 可以根据其中的文本保留自格式化的宽度和高度。 不幸的是,即使在故事板中修复宽度和高度也不能解决问题:更新文本仍然会触发重新布局所有内容的请求。

      我找到了使用 CATextLayer 的解决方案——它不会影响布局,您可以在其中放置任何您想要的文本。但这样做的代价是您必须以编程方式处理所有事情,请记住您处理的是真实像素,而不是点,如下所示:

                  let mScale = UIScreen.main.scale
      
                  indicatorLeft = CATextLayer ()
                  indicatorLeft.fontSize = FONT_SIZE * mScale
                  indicatorLeft.string = "text in CA layer"
                  indicatorLeft.foregroundColor = UIColor.white.cgColor
                  indicatorLeft.shadowColor = UIColor.gray.cgColor
                  indicatorLeft.isHidden = false
                  indicatorLeft.contentsScale = mScale
      
                  indicatorLeft.frame = CGRect(x: 0, y:  0,
                                       width: VIEW_WIDTH * mScale, height: VIEW_HEIGHT * mScale )
      
                  leftView.layer.addSublayer(indicatorLeft)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-01-26
        • 2018-07-01
        • 2020-08-26
        • 2012-05-03
        • 1970-01-01
        • 1970-01-01
        • 2013-04-15
        • 1970-01-01
        相关资源
        最近更新 更多