Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine when UIView has finished layout

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.

like image 842
Evgeniy Kleban Avatar asked Apr 20 '17 13:04

Evgeniy Kleban


People also ask

What is UIView life cycle?

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.

When can I call super layoutSubviews?

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.

Does layoutIfNeeded call layoutSubviews?

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.

What is a UIView?

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.

How does auto layout work in UIView?

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.

What is method viewdidlayoutsubviews in Android?

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.

How to override high performance auto layout?

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.

What is the difference between right view and left view in iOS?

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.


2 Answers

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);
}
like image 198
rob mayoff Avatar answered Oct 14 '22 01:10

rob mayoff


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

        }
    }
}
like image 35
Michał Ziobro Avatar answered Oct 13 '22 23:10

Michał Ziobro