Search code examples
iosobjective-ccocoa-touchloadinguiactivityindicatorview

Programmatically resizing/aligning loading view


Ok, I'm adding a loading screen to UITableViewController just like in the YouTube, AppStore or iTunes apps. And I'm doing it like this:

[self.view addSubview:[[LoadingView alloc] initWithFrame:self.view.bounds]];
dispatch_queue_t downloadQueue = dispatch_queue_create("downloader", NULL);
dispatch_async(downloadQueue, ^{
    //load data
    dispatch_async(dispatch_get_main_queue(), ^{
        [[self.view.subviews lastObject] removeFromSuperview]; 
        //reload data graphicaly
    });
});
dispatch_release(downloadQueue);

And LoadingView is a subclass of UIView that has a white background a spinner (UIActivityIndicatorView) and a label: "Loading…". Now I want to place this two thing in the center and make them somewhat "rock solid", so when you init that View on viewDidLoad and then it's superview resizes due to the presence of navigation bar and tab bar everything stays nicely centered. Oh and I want it to handle the rotation… etc.

Now here is how I've tried to do it:

#define LABEL_WIDTH 80

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setBackgroundColor:[UIColor whiteColor]];
        UIActivityIndicatorView* spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
        UIView *innerView = [[UIView alloc] initWithFrame:CGRectMake(0,
                                                                     0,
                                                                     spinner.frame.size.width + 5 + LABEL_WIDTH,
                                                                     spinner.frame.size.height)];
        innerView.center = self.center;
        CGRect transitionFrame = spinner.frame;
        transitionFrame.origin.x = innerView.frame.origin.x;
        transitionFrame.origin.y = innerView.frame.origin.y;
        spinner.frame = transitionFrame;
        transitionFrame.origin.x = transitionFrame.origin.x+5+spinner.frame.size.width;
        transitionFrame.size.width = LABEL_WIDTH;
        UILabel *label = [[UILabel alloc] initWithFrame:transitionFrame];
        label.text = @"Loading…";
        [innerView addSubview: spinner];
        [spinner startAnimating];
        [innerView addSubview: label];
        innerView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |
                                      UIViewAutoresizingFlexibleRightMargin |
                                      UIViewAutoresizingFlexibleTopMargin |
                                      UIViewAutoresizingFlexibleBottomMargin);
        [self addSubview:innerView];
        self.autoresizingMask = (UIViewAutoresizingFlexibleWidth|
                                 UIViewAutoresizingFlexibleHeight);
    }
    return self;
}

So the label and the spinner are set as subviews of some other view and that view is centered in our LoadingView and has all struts and springs unset, so after resizing it should stay in the middle. And LoadingView has all struts and springs set so it will change size together with it's superview

But it all isn't working as intended, and I feel like I'm on the bad path and my code is wrong. Can someone help me a bit and explain what I should take a look at?


Solution

  • Seems that

    innerView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |
                                  UIViewAutoresizingFlexibleRightMargin |
                                  UIViewAutoresizingFlexibleTopMargin |
                                  UIViewAutoresizingFlexibleBottomMargin);
    

    was bad idea to center inner views.

    I solved it by redefining layoutSubviews method.

    The code itself can be found at https://github.com/Uko/UILoadingView/blob/master/UILoadingView.m