Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: Changing UIView frame but display doesn't update

I'm trying to dynamically resize the frame of a UIView. I can update the frame in the code and see that the values have changed, but the actual display never changes, even though I run setNeedsDisplay().

I'm following this post, where I define a new frame via CGRectMake, and then set the UIView's frame to this new frame. i.e., I'm doing this...

let newFrame = CGRectMake(minX,minY, width, height)
VUBarCover.frame = newFrame

If I print out the value of the UIView's frame, I see that it's changing as I like. But the display (in the Simulator) never reflects these changes.

Any idea how to fix this?

Update: In greater detail: I'm trying to programmatically cover up one UIView with another. It's a horizontal "VU Meter", consisting of a base UIImageView showing a color gradient, that gets partially dynamically "covered" up by a UIView with a black background. The UIImageView "VUBarImageView" is defined in StoryBoard and is subject to AutoLayout. The black UIView "VUBarCover" is defined purely programmatically, with no AutoLayout constraints.

Here's the relevant parts of the code for completeness... The trouble I'm having is in updateVUMeter()...

@IBOutlet weak var VUBarImageView: UIImageView!
var VUBarCover : UIView!

// this gets called after AutoLayout is finished
    override func viewDidLayoutSubviews() {
        // cover up VU Meter completely
        let center = VUBarImageView.center
        let width = VUBarImageView.frame.width
        let height = VUBarImageView.frame.height
        let frame = VUBarImageView.frame
        VUBarCover = UIView(frame: frame)
        MainView.addSubview(VUBarCover)
        VUBarCover.backgroundColor = UIColor.blueColor() // use black eventually. blue lets us see it for testing
    }

 // partially cover up VUBarImage with black VUBarCover coming from the "right"
func updateVUMeter(inputValue:Float) {   //inputValue is in dB

    //let val = pow(10.0,inputValue/10)  // convert from dB
    let val = Float( Double(arc4random())/Double(UInt32.max) ) //for Simulator testing, just use random numbers between 0 and 1
    print("val = ",val)

    let fullWidth = VUBarImageView.frame.width
    let maxX = VUBarImageView.frame.maxX
    let width = fullWidth * CGFloat(1.0-val)
    let minX = maxX - width
    let minY = VUBarImageView.frame.minY
    let height = VUBarImageView.frame.height

    let newFrame = CGRectMake(minX,minY, width, height)
    VUBarCover.frame = newFrame
    VUBarCover.setNeedsDisplay()

    print("fullWidth = ",fullWidth,"width = ",width)
    print(" newFrame = ",newFrame,"VUBarCover.frame = ",VUBarCover.frame)
}

And sample results are:

val =  0.9177
fullWidth =  285.0 width =  23.4556210041046
newFrame =  (278.544378995895, 93.5, 23.4556210041046, 10.0) VUBarCover.frame =  (278.544378995895, 93.5, 23.4556210041046, 10.0)
val =  0.878985
fullWidth =  285.0 width =  34.4891595840454
newFrame =  (267.510840415955, 93.5, 34.4891595840454, 10.0) VUBarCover.frame =  (267.510840415955, 93.5, 34.4891595840454, 10.0)
val =  0.955011
fullWidth =  285.0 width =  12.8218790888786
newFrame =  (289.178120911121, 93.5, 12.8218790888786, 10.0) VUBarCover.frame =  (289.178120911121, 93.5, 12.8218790888786, 10.0)

...i.e., we see that VUBarCover.frame is changing 'internally', but on the screen it never changes. What we see instead is the full-size cover, completely covering the image below (ca. 300 pixels wide), even though its width should be only, about 12 pixels).

Even if I do a setNeedsDisplay() on the superview of VUBarCover, nothing changes.

Help? Thanks.

like image 690
sh37211 Avatar asked Mar 12 '23 01:03

sh37211


1 Answers

First, you have a few errors worth noting:

  • Instance variables (like VUBarCover and MainView should always begin with a lowercase letter (like mainView).
  • You should call super.viewDidLayoutSubviews() at the beginning of your override implementation

Second, viewDidLayoutSubviews() can be called any number of times, so your implementation of this method should be idempotent, meaning it has an identical effect no matter how many times it's called. But when you call:

VUBarCover = UIView(frame: frame)
MainView.addSubview(VUBarCover)

You are creating a new UIView and adding it to the view hierarchy, along with the others you may have added in the past. So it looks like the frame isn't updating on the screen. In fact, you just can't tell because the old views are behind the one you're updating and they're not changing.

Instead, you probably want to create this once:

var vuBarCover = UIView()

Add it as a subview in viewDidLoad():

mainView.addSubview(vuBarCover)

And modify its frame in viewDidLayoutSubviews():

vuBarCover.frame = vuBarImageView.frame

You don't need to call setNeedsDisplay().

like image 142
Aaron Brager Avatar answered Mar 20 '23 07:03

Aaron Brager