更新 UILabel 文本会重置故事板吗?
Updating UILabel Text resets storyboard?
我遇到了一个奇怪的场景,我自己想不通。
我开始了一个小测试场景,其中有一个几乎是空的 iOS 单视图应用程序和一个故事板。故事板包含一个标签和一个 10x10 的 UIView,仅用于展示问题。当然,他们有适当的 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 次只更改文本以更好地显示这里的问题。
如果我 运行 该代码点按预期向右移动,但是当我更新标签文本时,点视图会重置并跳回其原始位置。所以更新我的标签文本会重置我在 Storyboard 上的其他视图,这让我很困惑。
有人可以重现甚至解释一下吗?
提前谢谢大家:)
亚历克斯
我能够重现所描述的行为,这就是它发生的原因:
- 默认情况下,元素的位置由自动生成的 AutoLayout 约束决定
- 更改
dot
的位置时,您只是在更新它的当前帧,而不是基础约束
- 更改
UILabel
的文本可能会更改 UILabel
内容的大小,因此需要重新计算其框架。这里整个布局是根据约束重新计算的,因此也重置了 dot
. 的框架
为了解决,我建议正确指定AutoLayout约束并更新它们而不是直接定位。
差不多就是这样,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];
}
它起作用了,改变约束而不是实际视图现在将视图移到屏幕上。如果这是最佳实践,我不知道。如果我没有出于练习目的尝试过它,按照一个陈旧的教程,我一开始就不会想到它。
希望有一天这会有所帮助。欢迎提出任何意见或改进:)
亚历克斯
这似乎是一个老问题,但它也困扰着我。
我感到困惑的是,更改视觉层次结构底部 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)
我遇到了一个奇怪的场景,我自己想不通。
我开始了一个小测试场景,其中有一个几乎是空的 iOS 单视图应用程序和一个故事板。故事板包含一个标签和一个 10x10 的 UIView,仅用于展示问题。当然,他们有适当的 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 次只更改文本以更好地显示这里的问题。
如果我 运行 该代码点按预期向右移动,但是当我更新标签文本时,点视图会重置并跳回其原始位置。所以更新我的标签文本会重置我在 Storyboard 上的其他视图,这让我很困惑。
有人可以重现甚至解释一下吗?
提前谢谢大家:)
亚历克斯
我能够重现所描述的行为,这就是它发生的原因:
- 默认情况下,元素的位置由自动生成的 AutoLayout 约束决定
- 更改
dot
的位置时,您只是在更新它的当前帧,而不是基础约束 - 更改
UILabel
的文本可能会更改UILabel
内容的大小,因此需要重新计算其框架。这里整个布局是根据约束重新计算的,因此也重置了dot
. 的框架
为了解决,我建议正确指定AutoLayout约束并更新它们而不是直接定位。
差不多就是这样,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];
}
它起作用了,改变约束而不是实际视图现在将视图移到屏幕上。如果这是最佳实践,我不知道。如果我没有出于练习目的尝试过它,按照一个陈旧的教程,我一开始就不会想到它。
希望有一天这会有所帮助。欢迎提出任何意见或改进:)
亚历克斯
这似乎是一个老问题,但它也困扰着我。 我感到困惑的是,更改视觉层次结构底部 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)