I have UIView subclass, and for laying out UI elements i don't using Auto Layout. I want to set frame depends on width or height of frame. However, when i do this right after alloc]init]
methods, it look like self.frame.size.height or width is zero. For temporary solution i did following:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.25 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
UIImage *img = [UIImage imageNamed:@"sound_level_arrow"];
_arrowImgView = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.size.width/2, 0, img.size.width, img.size.height)];
_arrowImgView.center = CGPointMake(self.mainImgView.frame.size.width/2, self.mainImgView.frame.size.height-2);
_arrowImgView.image = [UIImage imageNamed:@"sound_level_arrow"];
_arrowImgView.contentMode = UIViewContentModeCenter;
[self.mainImgView addSubview:_arrowImgView];
});
But of course i need to know, when view is finish loading and where it has actual self.frame.size.width and height.
Every UIView with enabled Auto Layout passes 3 steps after initialization: update, layout and render. These steps do not occur in a one-way direction. It's possible for a step to trigger any previous one or even force the whole cycle to repeat.
You always have to call [super layoutSubviews] last, if the intrinsic content size of a view will be changed. If you change the title of the button, the intrinsic content size of the UIButton will be changed, therefore the last call.
layoutIfNeeded is another method on UIView that will trigger a layoutSubviews call in the future. Instead of queueing layoutSubviews to run on the next update cycle, however, the system will call layoutSubviews immediately if the view needs a layout update.
UIView can be defined as an object by using which we can create and manage the rectangular area on the screen. We can have any number of views inside a view to create a hierarchical structure of the UIViews. The UIView is managed by using the methods and properties defined in the UIView class that inherits UIKit.
Every UIView with enabled Auto Layout passes 3 steps after initialization: update, layout and render. These steps do not occur in a one-way direction. It’s possible for a step to trigger any previous one or even force the whole cycle to repeat. This step is all about calculating view frame based on its constrains.
Method viewDidLayoutSubviews is the most important among all. It is called to notify view controller that its view has finished the Layout step, i.e. it’s bounds have changed. This is an opportunity to make changes to a view after it has laid out its subviews, but before it becomes visible on the screen.
According to High Performance Auto Layout, the process happens top-down, i.e. the system traverses views from super- to subviews and calls layoutSubviews for each. layoutSubviews is the most common method to be overridden from the whole Auto Layout life cycle. You will do this when: Constraints are not enough to express view’s layout.
The right view has a shadow added by means of UIKit and its image contains only a circle. The key to understanding their positioning is the difference in alignment rectangles. Since shadow is a part of the left view’s image, it is also a part of its alignment rectangle.
If you want to do this in your custom view subclass, override layoutSubviews
in that class. Since iOS may send layoutSubviews
more than once, be careful to only create _arrowImgView
the first time:
- (void)layoutSubviews {
[super layoutSubviews];
if (_arrowImgView == nil) {
UIImage *image = [UIImage imageNamed:@"sound_level_arrow"];
_arrowImgView = [[UIImageView alloc] initWithImage:image];
_arrowImgView.contentMode = UIViewContentModeCenter;
[self addSubview:_arrowImgView];
}
CGRect myBounds = self.bounds;
CGRect mainFrame = self.mainImageView.frame;
_arrowImgView.bounds = CGRectMake(0, 0, myBounds.size.width, myBounds.size.height);
_arrowImgView.center = CGPointMake(mainFrame.size.width / 2, mainFrame.size.height - 2);
}
Use this one. It handles orientation changes, and redrawing of Gradient background.
import UIKit
@IBDesignable
final class GradientButton: UIButton {
@IBInspectable var startColor: UIColor = UIColor.clear
@IBInspectable var endColor: UIColor = UIColor.clear
override func draw(_ rect: CGRect) {
if let gradient = layer.sublayers?.first as? CAGradientLayer {
gradient.removeFromSuperlayer()
}
let gradient: CAGradientLayer = CAGradientLayer()
gradient.frame = CGRect(origin: CGPoint.zero, size: self.frame.size)
gradient.colors = [startColor.cgColor, endColor.cgColor]
layer.insertSublayer(gradient, at: 0)
super.draw(rect)
}
@IBInspectable var imageContentMode: Int {
get {
return imageView?.contentMode.rawValue ?? 0
}
set {
imageView?.contentMode = UIViewContentMode(rawValue: newValue) ?? .scaleAspectFit
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With