当应用程序进入后台状态时,layoutSubviews会被重复调用

时间:2016-05-27 15:37:57

标签: ios swift autolayout

我希望标签的字体大小与屏幕大小成正比。我已经将UILabel类子类化为完成此任务:

@IBDesignable class MyCustomLabel: UILabel {
    override func layoutSubviews() {
        super.layoutSubviews()
        self.font = UIFont(name: "myFontName", size: (self.font?.pointSize)!)
        self.adjustsFontSizeToFitWidth = true
    }
}

首次启动应用时,标签(与超级视图约束成比例的宽度)会正确调整大小,但是当它进入背景时,会重复调用layoutSubviews状态。该应用不再响应用户'输入并保持为字体指定字体大小。

Download test project.为什么会这样?

1 个答案:

答案 0 :(得分:4)

在你的测试项目中,如果我在iPhone上运行,我没有看到在后台调用layoutSubviews()。它只发生在iPad上。

那是因为您的应用支持多任​​务处理:

  • 当您的应用停用时,iOS会将其调整为不同大小,以便为应用切换器拍摄快照。这些调整大小会导致您的视图layoutSubviews()被调用。那是完全正常的。
  • iOS然后将您的应用返回到原始大小。

真正的问题是你正在创建一个“布局循环”。您在layoutSubviews()中的代码导致您自己的视图布局无效,因此系统需要再次运行布局过程。然后布局运行,再次执行,它会再次发生。

具体来说,原因是:

self.font = UIFont(name: fontName, size: fontSize)

更改标签的字体会导致其intrinsicSize发生变化,这意味着其超级视图可能需要更新其布局,因此布局过程需要再次运行。在layoutSubviews()中执行此操作是一个坏主意,因为它会导致布局循环。您实际上只应更改子视图的属性,而不是视图本身。

为什么您认为需要在layoutSubviews()中执行此操作?在布局过程之外,可能有一个更好的地方。在您的示例中,我没有看到此代码如何执行任何有用的操作。

设置adjustsFrameSizeToWidth一次更有意义,然后在layoutSubviews()中不做任何事情:

override init(frame: CGRect) {
    super.init(frame: frame)
    self.adjustsFontSizeToFitWidth = true
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    self.adjustsFontSizeToFitWidth = true
}

如果您尝试根据尺寸等级更改字体大小,可以通过覆盖traitCollectionDidChange()在代码中执行此操作:

override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)

    var fontSize: CGFloat
    if (self.traitCollection.horizontalSizeClass == .Regular) {
        fontSize = 70
    }
    else {
        fontSize = 30
    }
    self.font = UIFont(name: fontName, size:fontSize)
}