I made a custom UITextView
for vertical Mongolian writing. It is made by subclassing UIView
, which has a subview called rotatedView
. This rotatedView
itself has a UITextView
subview. So there are three views: a parent, a child, and a grandchild.
The UITextView
contains the actual text. The rotatedView
is a container that gives the text the correct vertical orientation and line wrapping. The parent UIView
is the container that allows autolayout to work normally in the Interface Builder. For more background, please see here and here.
Whenever the parent UIView
frame changes, the old rotatedView
is removed and a new instance is made. Then the UITextView
(a property of the custom UIView
subclass) is added to the rotatedView
.
What I am worried about is memory leaks. Since the grandchild view UITextView
is a property of my custom view but rotatedView
isn't. When I drop rotatedView
on a frame size change, is any reference kept to it since the UITextView
property is a subview of rotatedView
?
This demo project can be found on github, but here is the custom class code:
import UIKit
@IBDesignable class UIMongolTextView: UIView {
// ********* Unique to TextView *********
private let view = UITextView()
let mongolFontName = "ChimeeWhiteMirrored"
@IBInspectable var text: String {
get {
if let txt = view.text {
return txt
} else {
return ""
}
}
set {
view.text = newValue
}
}
func setup() {
view.text = self.text
view.backgroundColor = self.backgroundColor
view.font = UIFont(name: mongolFontName, size: 24)
}
// *******************************************
// ****** General code for Mongol views ******
// *******************************************
private var oldWidth: CGFloat = 0
private var oldHeight: CGFloat = 0
// This method gets called if you create the view in the Interface Builder
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// This method gets called if you create the view in code
override init(frame: CGRect){
super.init(frame: frame)
self.setup()
}
override func awakeFromNib() {
super.awakeFromNib()
self.setup()
}
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))
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, CGFloat(-1), CGFloat(1))
return transform
}
}
I am using the same general method for text views, labels, and table views.
Update
The advice below is extremely useful. The things I've already tried to apply can be seen in the code of this question.
However, no one has yet commented on my original reason for posting this question. That is, is there any sort of memory leak issue with removing and then recreating a subview that itself has a property subview? I'm going to take that to mean that there isn't a big problem with this, so I won't worry about it.