How to detect when UIView size is changed in swift ios xcode?
Solution 1
viewWillLayoutSubviews()
and viewDidLayoutSubviews()
will be called whenever the bounds change. In the view controller.
Solution 2
There's an answer here:
https://stackoverflow.com/a/27590915/5160929
Just paste this outside of a method body:
override var bounds: CGRect {
didSet {
// Do stuff here
}
}
Solution 3
You can also use KVO:
You can set a KVO like this, where view
is the view
you want to observe frame changes for:
self.addObserver(view, forKeyPath: "center", options: NSKeyValueObservingOptions.New, context: nil)
And you can get the changes with this notification:
override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: NSDictionary!, context: CMutableVoidPointer) {
}
The observeValueForKeyPath
will be called whenever the frame of the view you are observing changes.
Also remember to remove the observer when your view is about to be deallocated:
view.removeObserver(self, forKeyPath:"center")
Solution 4
You can create a custom class, and use a closure to get the updated rect comfortably. Especially handy when dealing with classes (like CAGradientLayer
which want you to give them a CGRect
):
GView.swift
:
import Foundation
import UIKit
class GView: UIView {
var onFrameUpdated: ((_ bounds: CGRect) -> Void)?
override func layoutSublayers(of layer: CALayer) {
super.layoutSublayers(of: layer)
self.onFrameUpdated?(self.bounds)
}
}
Example Usage:
let headerView = GView()
let gradientLayer = CAGradientLayer()
headerView.layer.insertSublayer(gradientLayer, at: 0)
gradientLayer.colors = [
UIColor.mainColorDark.cgColor,
UIColor.mainColor.cgColor,
]
gradientLayer.locations = [
0.0,
1.0,
]
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
headerView.onFrameUpdated = { _ in // here you have access to `bounds` and `frame` with proper values
gradientLayer.frame = headerView.bounds
}
If you are not adding your views through code, you can set the Custom Class property in storyboard to GView
.
Please note that the name GView
was chosen as a company measure and probably choosing something like FrameObserverView
would be better.
Solution 5
This is a simple and not-too-hacky solution: You remember the last size of your view, compare it to the new size in an overridden layoutSubviews
method, and then do something when you determine that the size has changed.
/// Create this as a private property in your UIView subclass
private var lastSize: CGSize = .zero
open override func layoutSubviews() {
// First call super to let the system update the layout
super.layoutSubviews()
// Check if:
// 1. The view is part of the view hierarchy
// 2. Our lastSize var doesn't still have its initial value
// 3. The new size is different from the last size
if self.window != nil, lastSize != .zero, frame.size != lastSize {
// React to the size change
}
lastSize = frame.size
}
Note that you don't have to include the self.window != nil
check, but I assume that in most cases you are only interested in being informed of size changes for views that are part of the view hierarchy.
Note also that you can remove the lastSize != .zero
check if you want to be informed about the very first size change when the view is initially displayed. Often we are not interested in that event, but only in subsequent size changes due to device rotation or a trait collection change.
Enjoy!
Joon. P
Updated on July 09, 2022Comments
-
Joon. P almost 2 years
I am trying some stuffs out with CATiledLayer inside UIScrollView.
Somehow, the size of UIView inside the UIScrollView gets changed to a large number. I need to find out exactly what is causing this resize.
Is there a way to detect when the size of UIView(either frame, bounds) or the contentSize of UIScrollView is resized?
I tried
override var frame: CGRect { didSet { println("frame changed"); } }
inside UIView subclass,
but it is only called once when the app starts, although the size of UIView is resized afterwards.
-
simons almost 9 yearsPerhaps I am misunderstanding. But AFAIK anything which is happening as a result of constraints should show in the layout preview - in the Assistant editor.
-
srik about 8 yearsKVO on UIKit may break at anytime. So should be used only as a last resort and be prepared for it to fail in future versions of iOS. Look at this answer from an iOS UIKit engineer: stackoverflow.com/a/6051404/251394
-
Yitzchak over 6 yearsBest for UIScrollView (because viewWillLayoutSubviews() is not accessible) Just notice that it may be called many times and be careful not to change the bounds in it, it will be infinitly
-
HuaTham almost 6 yearsUIKit properties are in general not KVO-compliant nor are they documented to be so. I'd stay away from this approach.
-
HuaTham almost 6 years
-
Womble almost 3 yearsIt's not that easy.
layoutSubviews
can be called multiple times. Also, the view'sframe
andbounds
properties may be .zero. So you have to check each time it is called, and act appropriately (e.g. remove/rebuild layers) -
Oliver Pearmain almost 3 yearsThis is a
UIView**Controller**
method, not aUIView
method like requested.