在layoutSubviews中旋转视图

8

背景和目标

目标:我想要旋转和翻转一个UITextView。(为什么:请参考我的上一个问题

问题:如果我直接对UITextView进行变换,由于某种未知原因,文本布局会被搞乱。

解决方案:UITextView放入一个UIView容器中,然后在容器上进行变换。

新问题:对旋转的视图进行自动布局(或任何类型的布局)成为一个主要的头痛

建议的解决方案:创建一个UIView的子类,作为旋转和翻转UIView的额外容器。然后,Auto Layout应该可以在这个自定义视图上正常工作。

enter image description here

当前问题

当所有内容首次出现时它可以工作(UITextView 具有黄色背景):

enter image description here

但是当屏幕方向改变时,以下情况会发生(蓝色是在IB中设置的子类化UIView背景):

enter image description here

如果我禁用rotationView.addSubview(textView)这一行,那么旋转容器视图(红色)在方向改变时仍然可以很好地重新定位:

enter image description here

所以问题肯定是我添加 UITextView 的位置有问题。但是我该怎么做呢?

代码

class MongolTextView: UIView {

    // properties
    var rotationView: UIView!
    var textView: UITextView!

    // 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()
    }

    func setup() {

        rotationView = UIView(frame: self.frame)
        rotationView.backgroundColor = UIColor.redColor()
        self.addSubview(rotationView)

        textView = UITextView(frame: CGRectZero)
        textView.backgroundColor = UIColor.yellowColor()
        textView.text = "This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text This is some text "

    }

    override func layoutSubviews() {
        super.layoutSubviews()

        // set the size of the rotation container view
        let width = self.bounds.width
        let height = self.bounds.height
        rotationView.frame = CGRect(origin: CGPoint(x: CGFloat(0), y: CGFloat(0)), size: CGSize(width: height, height: width))

        textView.frame = rotationView.bounds // Problem lines here???
        rotationView.addSubview(textView)    // Problem lines here???

        // rotate, translate, and flip the container view
        var rotation = CGAffineTransformMakeRotation(CGFloat(-M_PI_2))
        // the following translation repositions the top left corner at the origin of the superview
        var translation = CGAffineTransformMakeTranslation((rotationView.bounds.height / 2)-(rotationView.bounds.width / 2), (rotationView.bounds.width / 2)-(rotationView.bounds.height / 2))
        var rotationAndTranslation = CGAffineTransformConcat(rotation, translation)
        var transformPlusScale = CGAffineTransformScale(rotationAndTranslation, CGFloat(-1), CGFloat(1))

        rotationView.transform = transformPlusScale

    }
}

如果我无法让它工作...

虽然我目前遇到了难题,但我的下一个计划是覆盖 drawRect() 来执行转换。不过这不是我的首选,因为据说这样做会降低性能。

2个回答

9
问题在于问题中的代码似乎是把变换不断添加到一起。为了解决这个问题,解决方案是每次重置变换,即将其设置为身份变换。
rotationView.transform = CGAffineTransformIdentity

这是一个部分实现,展示了关键部分。
import UIKit
@IBDesignable class UIVerticalTextView: UIView {

    var textView = UITextView()
    let rotationView = UIView()

    var underlyingTextView: UITextView {
        get {
            return textView
        }
        set {
            textView = newValue
        }
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override init(frame: CGRect){
        super.init(frame: frame)
        self.setup()
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        self.setup()
    }

    func setup() {

        rotationView.backgroundColor = UIColor.redColor()
        textView.backgroundColor = UIColor.yellowColor()
        self.addSubview(rotationView)
        rotationView.addSubview(textView)

        // could also do this with auto layout constraints
        textView.frame = rotationView.bounds
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        rotationView.transform = CGAffineTransformIdentity // *** key line ***

        rotationView.frame = CGRect(origin: CGPointZero, size: CGSize(width: self.bounds.height, height: self.bounds.width))
        rotationView.transform = translateRotateFlip()
    }

    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
    }
}

我的最新实现最可能在这个github链接中找到。


2
在调试模式下,检查您的宽度和高度。我猜旋转前后它们是相同的。这样您的屏幕截图将会出现问题。
第二件事是您应用了两次变换。
您有旋转视图 -> 您应用变换 --> 您在手机旋转时更改框架 -> 先前的变换仍然适用 -> 然后您应用另一个变换。也许您的答案就在这里。
编辑:
可能的解决方案:
您可以检测您拥有哪种方向。
 UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;

 if(orientation == 0) //Default orientation 
   //UI is in Default (Portrait) -- this is really a just a failsafe. 
 else if(orientation == UIInterfaceOrientationPortrait)
   //Do something if the orientation is in Portrait
 else if(orientation == UIInterfaceOrientationLandscapeLeft)
   // Do something if Left
 else if(orientation == UIInterfaceOrientationLandscapeRight)

然后决定如何处理这个问题。

如果方向是纵向,则采取以下措施

width = self.bound.size.width;
height = self.bound.size.height;

如果不是
height = self.bound.size.width;
width = self.bound.size.height;

是的。self.boundswidthheight 保持不变。关于变换被应用两次的好点子。但我不太确定如何解决这个问题。 - Suragch
UITextView 的文本在应用旋转时需要改变吗?您能否只快照它并旋转快照呢? - Nick
@Nick,应用旋转时文本内容不需要改变,但需要保留UITextView可滚动的属性。在我看来,创建位图可能会导致其无法滚动。 - Suragch
@MarkoZadravec,我认为在self和子视图之间始终需要交换宽度和高度(对于任何方向),因为子视图相对于self旋转了90度。 - Suragch
您确认在旋转宽度和高度时它们是相同的,因此在一种情况下宽度是实际宽度,但在纵向模式下宽度是实际高度,高度也是如此。 - Marko Zadravec

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接