Swift: Changing UIView frame but display doesn't update
Solution 1
First, you have a few errors worth noting:
- Instance variables (like
VUBarCover
andMainView
should always begin with a lowercase letter (likemainView
). - 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()
.
Solution 2
In my case, there were animations left on the layer, after removing them, the frame changes are updated:
myView.layer.removeAllAnimations()
iOS displays presentationLayer
of a UIView
on the screen, animations are played on that layer (frame
updates goes there during animation).
Solution 3
I am making a custom UIView like you right now, maybe I can help.
Firstly, viewDidLayoutSubviews() like your comment: this gets called after AutoLayout is finished. And after every layout did set, you start to set new frame for your UIView and UIImage, maybe the bug is here. Please try to move your code in viewDidLayoutSubviews() to layoutSubviews(). And don't forget add super.layoutSubviews() in override function, which can help you away from hundreds of bugs :].
Secondly, only if you override the drawRect: method, then you should call setNeedsDisplay(), which is telling system that i need to redraw my view, and system will call drawRect: method appropriately.
Hope you can figure it out.
sh37211
They gave me a piece of paper, then a couple postdocs, then they gave me tenure. I'll answer your physics questions if you'll answer my programming and/or signal processing questions. ;-) My fields are relativity, numerical analysis, astrophysics, and acoustics. I program in...whatever's not Ruby, Lisp, or Objective C. I'd like to buy everyone on S.O. a beverage.
Updated on June 04, 2022Comments
-
sh37211 almost 2 years
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.