我遇到了一个奇怪的场景,我自己无法理解。
我开始了一个小测试场景,几乎是空的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次只更改一次文本,以便更好地显示问题。
如果我运行该代码,则点会按预期向右移动,但是当我更新标签文本时,点视图会重置并跳回其原始位置。因此,更新我的标签文本会重置我在故事板上的其他视图,这完全让我困惑。
有人可以复制甚至解释它吗?
提前感谢所有人:)
亚历
答案 0 :(得分:6)
我能够重现所描述的行为,这就是它发生的原因:
dot
的位置时,您只更新当前帧,而不是更新基础约束UILabel
的文字可能会更改UILabel
内容的大小,因此需要重新计算其框架。这里根据约束重新计算整个布局,因此也重置了dot
。对于解析,我建议正确指定AutoLayout约束并更新它们而不是直接更新位置。
答案 1 :(得分:2)
就是这样,AutoLayout约束推动视图总是回到它上面。原来的地方,我觉得哪种意义,这就是AutoLayout应该做的事情。
所以,因为我希望我的litte 10x10点视图在屏幕上移动(为了练习目的,我改变了我的代码。我试着解释它,目前缺乏声誉使我无法发布适当的图像。
我做了两个自定义AutoLayout约束,告诉它保持视图总是从superview左边120px和superview顶部100px。我还为每个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];
}
它有效,更改约束而不是实际视图现在可以在屏幕上移动视图。如果这是最佳做法,我不知道。如果我没有尝试过它来进行练习,那么在老化的教程之后,我就不会想到这一点。
希望这可能有一天会有所帮助。欢迎提出任何意见或改进:)
亚历
答案 2 :(得分:0)
这似乎是一个老问题,但也困扰着我。 令我感到困惑的是,在可视层次结构的底部更改UILabel中的文本会使整个情节提要板重新布局。
更重要的是,由于具有一定的Android开发背景,所以我从未见过(类似)TextView在Android中的这种行为。您可以按自己的意愿更改其中的文本-永远不会更新更高级别的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)