背景:我回答了一个关于如何使用简单的演示代码创建文本视图内容的模糊图像的用户问题。
然后,我决定推广我的解决方案并重构我的演示应用程序,以创建一个具有
BlurringView
bool 和子视图的 UIView 的 blur
自定义子类。它管理一个 UIImageView
,如果 blur
布尔值为 true,它将显示子视图内容的模糊版本。
它工作得相当好,但是当子视图是
UITextView
时,我遇到了一个无法干净解决的问题:
如果旋转设备,文本视图的边界会发生变化,并且文本视图会在新边界中重新排列文本,但仅在设备旋转动画完成后。
当
BlurringView
调整大小时,有多种方法可以收到通知:您可以将 didSet 添加到视图的边界;对于设备旋转,您可以实现 traitCollectionDidChange()
方法,您可以侦听设备旋转方法,等等。
但是,所有这些方法都会在文本视图将其内容重新流入新边界之前触发,因此,如果我在被任何这些方法调用时立即更新模糊视图,我将无法渲染模糊视图更新文本布局的版本。
我能够正确更新模糊内容的唯一方法是在被告知文本视图已更改(通过 TraitCollectionDidChange,一个更改)后延迟更新模糊视图 0.33 秒(1/3 秒)视图的边界、旋转通知或任何其他检测更改的方法。)
有没有一种干净的方法可以知道文本视图何时完成将其文本内容重新流入新的边界框? (当然,当我的子视图是
UITextView
时,可能需要添加特殊情况处理。)
我的示例项目是这里。目前它使用
traitCollectionDidChange()
方法来检测文本视图已更新,然后等待 0.33 秒才更新模糊视图:
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
print("In \(#function))")
//The below works to update the blur image after the TextView's frame changes, but only if we add a "magic number" delay of .33 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 0.33) {
print("Updating blur image. Bounds = \(self.bounds)")
self.updateBlurImage()
}
}
同样的 0.33 秒延迟方法也适用于视图边界上的
didSet
,或者如果我监听设备旋转通知。
寻找视图边界的变化可能是当前“hacky”方法的最佳位置,因为即使
BlurringView
在由于设备旋转之外的某种原因而调整大小的上下文中使用,也会被触发。
可能值得一试...
将“模糊”代码从 switch 操作移至函数:
func updateBlur() {
let renderer = UIGraphicsImageRenderer(size: textView.bounds.size)
let image = renderer.image { (context) in
var bounds = textView.bounds
bounds.origin = CGPoint.zero
textView.drawHierarchy(in: bounds, afterScreenUpdates: true)
}
blurView.image = blur(image: image, withRadius: blurRadius );
blurView.isHidden = false
}
扩展视图控制器以符合
NSLayoutManagerDelegate
:
extension ViewController: NSLayoutManagerDelegate {
func layoutManager(_ layoutManager: NSLayoutManager, didCompleteLayoutFor textContainer: NSTextContainer?, atEnd layoutFinishedFlag: Bool) {
self.updateBlur()
}
}
在
viewDidLoad()
:
textView.layoutManager.delegate = self
结果看起来好多了
blurView.contentMode = .scaleToFill