Медленное время загрузки пользовательского UIView в Swift
Фон
Для того, чтобы сделать текстовое представление, которое прокручивается горизонтально для вертикального монгольского шрифта, я сделал пользовательский UIView
подкласс. Класс занимает UITextView
ставит его в UIView
, поворачивает и переворачивает это представление, а затем помещает это представление в родительский UIView
,
Целью поворота и переворачивания является то, что текст будет вертикальным, а перенос строк будет работать правильно. Цель вклеить все в родителя UIView
Это так, что Авто макет будет работать в раскадровке. (Подробнее см. Здесь.)
Код
Я получил рабочее решение. Полный код на github находится здесь, но я создал новый проект и удалил весь ненужный код, который мог, чтобы изолировать проблему. Следующий код все еще выполняет основную функцию, описанную выше, но также имеет проблему медленной загрузки, описанную ниже.
import UIKit
@IBDesignable class UIMongolTextView: UIView {
private var view = UITextView()
private var oldWidth: CGFloat = 0
private var oldHeight: CGFloat = 0
@IBInspectable var text: String {
get {
return view.text
}
set {
view.text = newValue
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect){
super.init(frame: frame)
}
override func sizeThatFits(size: CGSize) -> CGSize {
// swap the length and width coming in and going out
let fitSize = view.sizeThatFits(CGSize(width: size.height, height: size.width))
return CGSize(width: fitSize.height, height: fitSize.width)
}
override func layoutSubviews() {
super.layoutSubviews()
// layoutSubviews gets called multiple times, only need it once
if self.frame.height == oldHeight && self.frame.width == oldWidth {
return
} else {
oldWidth = self.frame.width
oldHeight = self.frame.height
}
// Remove the old rotation view
if self.subviews.count > 0 {
self.subviews[0].removeFromSuperview()
}
// setup rotationView container
let rotationView = UIView()
rotationView.frame = CGRect(origin: CGPointZero, size: CGSize(width: self.bounds.height, height: self.bounds.width))
rotationView.userInteractionEnabled = true
self.addSubview(rotationView)
// transform rotationView (so that it covers the same frame as self)
rotationView.transform = translateRotateFlip()
// add view
view.frame = rotationView.bounds
rotationView.addSubview(view)
}
func translateRotateFlip() -> CGAffineTransform {
var transform = CGAffineTransformIdentity
// translate to new center
transform = CGAffineTransformTranslate(transform, (self.bounds.width / 2)-(self.bounds.height / 2), (self.bounds.height / 2)-(self.bounds.width / 2))
// rotate counterclockwise around center
transform = CGAffineTransformRotate(transform, CGFloat(-M_PI_2))
// flip vertically
transform = CGAffineTransformScale(transform, -1, 1)
return transform
}
}
проблема
Я заметил, что пользовательский вид загружается очень медленно. Я новичок в Xcode Instruments, поэтому я посмотрел полезные видео " Отладка проблем памяти" с помощью Xcode, Profiler и Time Profiler.
После этого я попытался найти проблему в моем собственном проекте. Кажется, что независимо от того, использую ли я Time Profiler или инструменты Leaks или Allocations, все они показывают, что мой класс init
метод делает слишком много работы. (Но я вроде знал это уже по времени медленной загрузки.) Вот скриншот из инструмента Allocations:
Я не раскрыл все дерево вызовов, потому что оно не подошло бы. Почему создается так много объектов? Когда я делал трехслойный пользовательский вид, я знал, что он не идеален, но число слоев, которые появляются в дереве вызовов, просто смешно. Что я делаю неправильно?
1 ответ
Вы не должны добавлять или удалять любые подпредставления внутри layoutSubviews
, так как это вызывает вызов layoutSubviews
снова.
Создайте свое подпредставление, когда вы создаете свое представление, а затем только корректируйте его положение в layoutSubviews
вместо удаления и повторного добавления.