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

Posted

技术标签:

【中文标题】应用程序进入后台状态时重复调用 layoutSubviews【英文标题】:layoutSubviews getting called repeatedly when the app enters background state 【发布时间】:2016-05-27 15:37:56 【问题描述】:

我希望标签的字体大小与屏幕大小成正比。我将UILabel 类子类化以完成此操作:

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

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

为什么会这样?

【问题讨论】:

感谢您提出这个问题! 【参考方案1】:

在您的测试项目中,如果我在 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)

【讨论】:

谢谢库尔特!有没有办法在您的解决方案中以编程方式设置自定义字体?您可能知道,自定义字体不适用于 Size Classes(***.com/questions/26166737/… 供参考)。 你的意思是自定义字体不能很好地在Interface Builder 中支持大小类。大小类始终存在,您始终可以使用 self.traitCollection.horizontalSizeClassself.traitCollection.verticalSizeClass 获取它们,并且您始终可以覆盖 traitCollectionDidChange() 以使用它们。无论如何,这就是 IB 支持为您所做的一切。我将添加一个示例。 感谢库尔特的精彩回答!一个真正的问题,你解决了它。

以上是关于应用程序进入后台状态时重复调用 layoutSubviews的主要内容,如果未能解决你的问题,请参考以下文章

当应用程序进入后台时,URLSessions 给 URLResponse nil

Swift - 当应用程序进入后台状态时,从 AppDelegate 更新 RootViewController 中的值

“applicationWillTerminate”没有被迅速调用

进入后台状态ios swift时防止websocket连接断开

每次我的应用程序从后台进入前台时,IOS Default.png 都会显示 [重复]

当应用程序移至后台状态并再次移至前台状态时,无限列表会导致重复