[英]Efficient off-screen UIView rendering and mirroring
我有一個“屏幕外”的UIView層次結構,我希望在屏幕的不同位置進行渲染。 此外,應該可以僅顯示此視圖層次結構的一部分,並且應該反映對此層次結構所做的所有更改。
困難:
UIView
方法drawHierarchy(in:afterScreenUpdates:)
總是調用draw(_ rect:)
,因此如果要將所有更改合並到視圖層次結構中,則對於大型層次結構效率非常低。 您必須在每次屏幕更新時重繪它或觀察所有視圖的所有更改屬性。 繪制視圖層次結構文檔 UIView
方法snapshotView(afterScreenUpdates:)
也沒有多大幫助,因為如果這個層次結構是“屏幕外”,我還沒有找到一種方法來獲得正確的視圖層次結構圖。 快照視圖文檔 “屏幕外”:此視圖層次結構的根視圖不是應用程序UI的一部分。 它沒有超級視圖。
您可以在下面看到我的想法的直觀表示:
這是我將如何去做。 首先,我會復制您嘗試復制的視圖。 我為此寫了一點擴展:
extension UIView {
func duplicate<T: UIView>() -> T {
return NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: self)) as! T
}
func copyProperties(fromView: UIView, recursive: Bool = true) {
contentMode = fromView.contentMode
tag = fromView.tag
backgroundColor = fromView.backgroundColor
tintColor = fromView.tintColor
layer.cornerRadius = fromView.layer.cornerRadius
layer.maskedCorners = fromView.layer.maskedCorners
layer.borderColor = fromView.layer.borderColor
layer.borderWidth = fromView.layer.borderWidth
layer.shadowOpacity = fromView.layer.shadowOpacity
layer.shadowRadius = fromView.layer.shadowRadius
layer.shadowPath = fromView.layer.shadowPath
layer.shadowColor = fromView.layer.shadowColor
layer.shadowOffset = fromView.layer.shadowOffset
clipsToBounds = fromView.clipsToBounds
layer.masksToBounds = fromView.layer.masksToBounds
mask = fromView.mask
layer.mask = fromView.layer.mask
alpha = fromView.alpha
isHidden = fromView.isHidden
if let gradientLayer = layer as? CAGradientLayer, let fromGradientLayer = fromView.layer as? CAGradientLayer {
gradientLayer.colors = fromGradientLayer.colors
gradientLayer.startPoint = fromGradientLayer.startPoint
gradientLayer.endPoint = fromGradientLayer.endPoint
gradientLayer.locations = fromGradientLayer.locations
gradientLayer.type = fromGradientLayer.type
}
if let imgView = self as? UIImageView, let fromImgView = fromView as? UIImageView {
imgView.tintColor = .clear
imgView.image = fromImgView.image?.withRenderingMode(fromImgView.image?.renderingMode ?? .automatic)
imgView.tintColor = fromImgView.tintColor
}
if let btn = self as? UIButton, let fromBtn = fromView as? UIButton {
btn.setImage(fromBtn.image(for: fromBtn.state), for: fromBtn.state)
}
if let textField = self as? UITextField, let fromTextField = fromView as? UITextField {
if let leftView = fromTextField.leftView {
textField.leftView = leftView.duplicate()
textField.leftView?.copyProperties(fromView: leftView)
}
if let rightView = fromTextField.rightView {
textField.rightView = rightView.duplicate()
textField.rightView?.copyProperties(fromView: rightView)
}
textField.attributedText = fromTextField.attributedText
textField.attributedPlaceholder = fromTextField.attributedPlaceholder
}
if let lbl = self as? UILabel, let fromLbl = fromView as? UILabel {
lbl.attributedText = fromLbl.attributedText
lbl.textAlignment = fromLbl.textAlignment
lbl.font = fromLbl.font
lbl.bounds = fromLbl.bounds
}
if recursive {
for (i, view) in subviews.enumerated() {
if i >= fromView.subviews.count {
break
}
view.copyProperties(fromView: fromView.subviews[i])
}
}
}
}
要使用此擴展,只需這樣做
let duplicateView = originalView.duplicate()
duplicateView.copyProperties(fromView: originalView)
parentView.addSubview(duplicateView)
然后我會屏蔽重復的視圖,只獲得你想要的特定部分
let mask = UIView(frame: CGRect(x: 0, y: 0, width: yourNewWidth, height: yourNewHeight))
mask.backgroundColor = .black
duplicateView.mask = mask
最后,我會使用CGAffineTransform將其縮放到您想要的任何大小
duplicateView.transform = CGAffineTransform(scaleX: xScale, y: yScale)
copyProperties函數應該可以正常工作,但如果需要可以將其更改為從一個視圖復制到另一個視圖。
祝你好運,讓我知道它是怎么回事:)
我會復制我想要顯示的內容並根據需要裁剪它。
假設我有一個ContentViewController
,它帶有我希望復制的視圖層次結構。 我將封裝可以對ContentViewModel
的層次結構進行的所有更改。 就像是:
struct ContentViewModel {
let actionTitle: String?
let contentMessage: String?
// ...
}
class ContentViewController: UIViewController {
func display(_ viewModel: ContentViewModel) { /* ... */ }
}
使用ClippingView
(或簡單的UIScrollView
):
class ClippingView: UIView {
var contentOffset: CGPoint = .zero // a way to specify the part of the view you wish to display
var contentFrame: CGRect = .zero // the actual size of the clipped view
var clippedView: UIView?
override init(frame: CGRect) {
super.init(frame: frame)
clipsToBounds = true
}
override func layoutSubviews() {
super.layoutSubviews()
clippedView?.frame = contentFrame
clippedView?.frame.origin = contentOffset
}
}
和一個視圖控制器容器,我會裁剪我的內容的每個實例,並在每次發生時更新所有內容:
class ContainerViewController: UIViewController {
let contentViewControllers: [ContentViewController] = // 3 in your case
override func viewDidLoad() {
super.viewDidLoad()
contentViewControllers.forEach { viewController in
addChil(viewController)
let clippingView = ClippingView()
clippingView.clippedView = viewController.view
clippingView.contentOffset = // ...
viewController.didMove(to: self)
}
}
func somethingChange() {
let newViewModel = ContentViewModel(...)
contentViewControllers.forEach { $0.display(newViewModel) }
}
}
這種情況可以適用於您的情況嗎?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.