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

Ces*_*are 4 ios autolayout swift

我希望标签的字体大小与屏幕大小成正比。我已经对这个UILabel类进行了子类化来完成这个:

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

标签(与附加到它们的超级视图约束成比例的宽度)在应用程序首次启动时正确调整大小,但是当它进入后台状态时layoutSubviews会被重复调用。该应用程序不再响应用户的输入并不断为字体分配字体大小。

为什么会这样?

Kur*_*vis 6

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

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

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

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

具体来说,原因是:

self.font = UIFont(name: fontName, size: fontSize)
Run Code Online (Sandbox Code Playgroud)

更改标签的字体会导致其内在尺寸发生变化,这意味着其超视图可能需要更新其布局,因此需要再次运行布局过程。这样做是个坏主意,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
}
Run Code Online (Sandbox Code Playgroud)

如果您尝试根据大小类更改字体大小,则可以通过覆盖在代码中执行此操作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)
}
Run Code Online (Sandbox Code Playgroud)