iosuiviewconstraintssubviews

Adding Subviews/Constraints: Trying to understand WHY my subviews are not showing up


I wanted to create a popup to let users know if they attempt to input duplicate info. So initially I had a UILabel that would pop up and disappear.

        UILabel *toastView = [[UILabel alloc] init];

        toastView.alpha = 0;
        toastView.layer.cornerRadius = 4.0f;
        toastView.layer.borderColor = [UIColor colorWithRed:200.0/255.0 green:199.0/255.0 blue:204.0/255.0 alpha:1].CGColor;
        CGFloat scale = [[UIScreen mainScreen] scale];
        if (scale == 2.0)  {
            // retina screen;
            toastView.layer.borderWidth = 0.5;
        } else {
            // non-retina screen
            toastView.layer.borderWidth = 1.0;
        }
        toastView.backgroundColor = [UIColor colorWithRed:63.0/255.0 green:63.0/255.0 blue:63.0/255.0 alpha:1];
        toastView.textAlignment = NSTextAlignmentCenter;
        [toastView setAdjustsFontSizeToFitWidth:YES];
        toastView.text = @" E-Mail has already been added  ";
        toastView.font = [UIFont fontWithName:@"HelveticaNeue" size:toastView.font.pointSize];
        toastView.textColor = [UIColor whiteColor];
        [toastView setTranslatesAutoresizingMaskIntoConstraints:NO];
        [toastView.layer setMasksToBounds:YES];
        [self.superview.superview.superview addSubview:toastView];
        [[UIApplication sharedApplication].keyWindow bringSubviewToFront:toastView];

        [toastView.bottomAnchor constraintEqualToAnchor:self.superview.superview.superview.centerYAnchor].active = YES;
        [toastView.centerXAnchor constraintEqualToAnchor:self.superview.superview.superview.centerXAnchor].active = YES;
        [toastView.heightAnchor constraintEqualToConstant:round(self.superview.superview.superview.frame.size.height/12)].active = YES;
        [toastView.widthAnchor constraintEqualToConstant:round(self.superview.superview.superview.frame.size.width/1.5)].active = YES;

        [UIView animateWithDuration:0.3 animations:^{
            toastView.alpha = 0.95;
        } completion:^(BOOL finished) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
                [UIView animateWithDuration:0.3 animations:^{
                    toastView.alpha = 0;
                } completion:nil];
            });
        }];

This worked fine. I then wanted to add an image so I decided to move the creation of this popup to a method that takes a view and a message and produces a popup view with a UILabel and UIImageView in it. Here is the code:

+(void)toastAlert:(UIView*)view message:(NSString*)message {
    UIView *toastView = [[UIView alloc] init];
    UILabel *toastLabel = [[UILabel alloc] init];
    UIImage *infoImage = [UIImage imageNamed:@"info_white"];
    UIImageView *imageView = [[UIImageView alloc] initWithImage:infoImage];

    toastView.alpha = 0;
    toastView.layer.cornerRadius = 4.0f;
    toastView.layer.borderColor = [UIColor colorWithRed:200.0/255.0 green:199.0/255.0 blue:204.0/255.0 alpha:1].CGColor;
    CGFloat scale = [[UIScreen mainScreen] scale];
    if (scale == 2.0)  {
        // retina screen;
        toastView.layer.borderWidth = 0.5;
    } else {
        // non-retina screen
        toastView.layer.borderWidth = 1.0;
    }

    [toastLabel setTextAlignment:NSTextAlignmentCenter];
    [toastLabel setAdjustsFontSizeToFitWidth:YES];
    [toastLabel setText:[NSString stringWithFormat:@"%@", message]];
    [toastLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:toastLabel.font.pointSize]];
    [toastLabel setTextColor: [UIColor whiteColor]];

    toastView.backgroundColor = [UIColor colorWithRed:63.0/255.0 green:63.0/255.0 blue:63.0/255.0 alpha:1];
    [toastView setTranslatesAutoresizingMaskIntoConstraints:NO];
    [toastView.layer setMasksToBounds:YES];
    [view addSubview:toastView];
    [[UIApplication sharedApplication].keyWindow bringSubviewToFront:toastView];
    [toastView addSubview:toastLabel];
    [toastView addSubview:imageView];

    [toastView.bottomAnchor constraintEqualToAnchor:view.centerYAnchor].active = YES;
    [toastView.centerXAnchor constraintEqualToAnchor:view.centerXAnchor].active = YES;
    [toastView.heightAnchor constraintEqualToConstant:round(view.frame.size.height/12)].active = YES;
    [toastView.widthAnchor constraintEqualToConstant:round(view.frame.size.width/1.5)].active = YES;

    [imageView.centerYAnchor constraintEqualToAnchor:toastView.centerYAnchor].active = YES;
    [imageView.leadingAnchor constraintEqualToAnchor:toastView.leadingAnchor constant:padding].active = YES;
    [imageView.heightAnchor constraintEqualToConstant:toastView.frame.size.height/2].active = YES;
    [imageView.widthAnchor constraintEqualToAnchor:toastView.heightAnchor].active = YES;


    [toastLabel.centerYAnchor constraintEqualToAnchor:toastView.centerYAnchor].active = YES;
    [toastLabel.centerXAnchor constraintEqualToAnchor:toastView.centerXAnchor constant:imageView.frame.size.width + padding*2].active = YES;
    [toastLabel.leadingAnchor constraintEqualToAnchor:imageView.trailingAnchor constant:padding].active = YES;
    [toastLabel.heightAnchor constraintEqualToConstant:toastView.frame.size.height/2].active = YES;


    [UIView animateWithDuration:0.3 animations:^{
        toastView.alpha = 0.95;
    } completion:^(BOOL finished) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            [UIView animateWithDuration:0.3 animations:^{
                toastView.alpha = 0;
            } completion:nil];
        });
    }];

}

No matter what I do, however, I can only make the UIView show up, no text or UILabel ever shows up. When I add the UIImageView, it shows up but messes up the resizing of the whole toastView.

I am still a junior developer and am sure I missed something basic...can anyone explain to me what I did incorrectly? (not just looking for a straight answer as the explanation is worth much more)


Solution

  • So I think your autoresizing masks were conflicting with your constraints. That and starting with a CGRectZero. I will try to address each item so you know what went wrong. Here is working code.

     -(void)alteredToastAlert:(UIView*)targetView message:(NSString*)message {
    
        CGFloat padding = 10.0;
        // i like to start with real frames buggy things happen with CGRectZero
        UIView *toastView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 300, 200)];
        //turn all resizing masks off
        [toastView setTranslatesAutoresizingMaskIntoConstraints:NO];
    
        UILabel *toastLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, 200)];
        [toastLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
    
        UIImage *infoImage = [UIImage imageNamed:@"info_white"];
        UIImageView *imageView = [[UIImageView alloc] initWithImage:infoImage];
        [imageView setTranslatesAutoresizingMaskIntoConstraints:NO];
        //has a size but intrinsic
    
        toastView.alpha = 0;
        toastView.layer.cornerRadius = 4.0f;
        toastView.layer.borderColor = [UIColor colorWithRed:200.0/255.0 green:199.0/255.0 blue:204.0/255.0 alpha:1].CGColor;
        CGFloat scale = [[UIScreen mainScreen] scale];
    
        if (scale == 2.0)  {
            // retina screen;
            toastView.layer.borderWidth = 0.5;
        } else {
            // non-retina screen
            toastView.layer.borderWidth = 1.0;
        }
    
        [toastLabel setTextAlignment:NSTextAlignmentCenter];
        [toastLabel setText:[NSString stringWithFormat:@"%@", message]];
        [toastLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:22]];
        [toastLabel setTextColor: [UIColor whiteColor]];
    
        [toastLabel setMinimumScaleFactor:0.01];
    
        toastView.backgroundColor = [UIColor colorWithRed:63.0/255.0 green:63.0/255.0 blue:63.0/255.0 alpha:1];
        [toastView setTranslatesAutoresizingMaskIntoConstraints:NO];
        [toastView.layer setMasksToBounds:YES];
        [targetView addSubview:toastView];
        [[UIApplication sharedApplication].keyWindow bringSubviewToFront:toastView];
        //adding here but everything has a frame so it is not size 0.  Then apply autolayout
        [toastView addSubview:toastLabel];
        [toastView addSubview:imageView];
    
        [toastView.bottomAnchor constraintEqualToAnchor:targetView.centerYAnchor].active = YES;
        [toastView.centerXAnchor constraintEqualToAnchor:targetView.centerXAnchor].active = YES;
        [toastView.heightAnchor constraintEqualToConstant:round(targetView.frame.size.height/12)].active = YES;
        [toastView.widthAnchor constraintEqualToConstant:round(targetView.frame.size.width/1.5)].active = YES;
    
        [imageView.centerYAnchor constraintEqualToAnchor:toastView.centerYAnchor].active = YES;
        [imageView.leadingAnchor constraintEqualToAnchor:toastView.leadingAnchor constant:padding].active = YES;
        [imageView.heightAnchor constraintEqualToConstant:toastView.frame.size.height/2].active = YES;
        [imageView.widthAnchor constraintEqualToAnchor:toastView.heightAnchor].active = YES;
        imageView.backgroundColor = [UIColor redColor];
    
    
    
        [toastLabel.centerYAnchor constraintEqualToAnchor:toastView.centerYAnchor].active = YES;
        [toastLabel.leadingAnchor constraintEqualToAnchor:imageView.trailingAnchor constant:padding].active = YES;
       // have to have a trailing to keep it inside the view
        [toastLabel.trailingAnchor constraintEqualToAnchor:toastView.trailingAnchor constant:padding].active = YES;
        [toastLabel.heightAnchor constraintEqualToConstant:toastView.frame.size.height/2].active = YES;
    
        toastView.alpha = 1;
        [UIView animateWithDuration:0.3 animations:^{
            toastView.alpha = 0.95;
        } completion:^(BOOL finished) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
                [UIView animateWithDuration:0.3 animations:^{
                    toastView.alpha = 0;
                } completion:^(BOOL finished){
                    //may need to remove from superview
                }];
            });
        }];
    
    }
    

    1) You will notice I set frames to start with. I do this because I notice starting with CGRectZero does some crazy things. We will eventually let the views be governed by auto layout but to start I set the frames. The imageview will establish it's intrinsic frame.

    2) Next I tell the system to not create constraints from the autoresizing masks. I do this for the toastView as will as the label and imageview.

    3) I tell the label a minimum scale factor for the text and I start with a value of 22 for font size that is arbitrary. You might bump it to 50 and it will still scale down. I think because of CGRectZero your font size previously was 0 but I am not sure

    4) I let your constraints do the work with the edition of maybe a trailing constraint on the label but I think that is it. You can look it over.

    5) When all of this is finished don't forget to remove the toastView from it's superview or the view you are passing because even though the alpha is 0 it is still there.