Search code examples
iosuiimageviewsubview

Subclass UIImageView layoutSubviews() vs. init()


I found a problem whereby the repeated calls to layoutSubviews(), in which I created my subviews (of UIImageView) and attached the necessary animations, caused the animations to constantly repeat without the possibility of stopping them. So, it became necessary to find another way to achieve the creation of the subviews and the attaching of the animations.

The obvious thing to do would be to move the layoutSubviews() code

    override func layoutSubviews() {
        super.layoutSubviews()
        print("layoutSubviews()")
        spindleLeft = addSpindle(index: 1, posX: 505, posY: 350)
        spindleRight = addSpindle(index: 2, posX: 1610, posY: 350)

        spinSpindle(spindle: spindleLeft)
        spinSpindle(spindle: spindleRight)
    }

to the init

    init(cassetteType: CassetteType) {
        self.cassetteType = cassetteType
        super.init(image: UIImage(named: self.cassetteType.rawValue + "Cassette"))
        self.translatesAutoresizingMaskIntoConstraints = false
        self.isUserInteractionEnabled = true
        self.contentMode = .scaleAspectFit

        print("init(cassetteType:)")

        spindleLeft = addSpindle(index: 1, posX: 505, posY: 350)
        spindleRight = addSpindle(index: 2, posX: 1610, posY: 350)

        spinSpindle(spindle: spindleLeft)
        spinSpindle(spindle: spindleRight)
    }

However, when I do this, the spindle subviews don't show up anymore. They do appear to have been created and the animations are applied but they are no longer visible.

Why would this be the case? How do the layoutSubviews and init differ in this regard?

The addSpindle() code creates each subview like so:

    func addSpindle(index: Int, posX: CGFloat, posY: CGFloat) -> UIImageView {
        let cassetteBounds = self.bounds
        let cassetteWidth = cassetteBounds.width
        let cassetteHeight = cassetteBounds.height

        let cassetteImageWidth =  self.image!.size.width
        let cassetteImageHeight = self.image!.size.height

        var scale = cassetteWidth / cassetteImageWidth

        var spindleWidth = cassetteWidth
        var spindleHeight = cassetteImageHeight*scale
        var spindleX = 0 as CGFloat
        var spindleY = (cassetteHeight - spindleHeight)/2

        if spindleHeight > cassetteHeight {
            scale = cassetteHeight / cassetteImageHeight
            spindleHeight = cassetteHeight
            spindleWidth = cassetteImageWidth*scale
            spindleY = 0 as CGFloat
            spindleX = (cassetteWidth - spindleWidth)/2
        }

        let spindle = UIImageView(image: UIImage(named: self.cassetteType.rawValue + "Spindle"))
        self.addSubview(spindle)

        spindle.frame.origin.x = spindleX + (posX*scale)
        spindle.frame.origin.y = spindleY + (posY*scale)
        spindle.frame.size.width = spindle.image!.size.width*scale //299.0
        spindle.frame.size.height = spindle.image!.size.height*scale //299.0

        let gesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
        gesture.numberOfTouchesRequired = 1
        gesture.minimumPressDuration = 0

        spindle.isUserInteractionEnabled = true
        spindle.isExclusiveTouch = true
        spindle.tag = index
        spindle.addGestureRecognizer(gesture)

        return spindle
    }

Solution

  • You're using self.bounds in your addSpindle method. As the view is not laid out yet on init, it's bounds are not determined yet.

    You may create your spindle views on init but you need to set their frames on layoutSubviews when the parent's bounds are calculated.