Search code examples
iosdynamicuibuttonpositioning

Dynamic UIButton positioning?


I have rewards that a user can get in my game. Now they only show if they unlock them. There are 4 different rewards they can get. Now they may have 1 reward unlocked or 2 or 3 or 4. However I want the images to be positioned dynamically so it looks like the image below. In the image below, each line should look the way it is with the number of rewards unlocked. enter image description here

Now, how would I go upon doing this? (Note: The images are not changing on the Y axis, they will just be dynamically positioned on the X axis, so don't be fooled by the image)

Thanks!


Solution

  • I would create a custom UIView subclass that does this so that you can use it anywhere that a view can be used.

    Usage:

    MyFlexibleSpacedButtons *myButtons = [[MyFlexibleSpacedButtons alloc] initWithFrame:CGRectMake(50, 50, 250, 60)];
    [myButtons setButtonAtIndex:2 hidden:NO];
    [myButtons setButtonAtIndex:0 hidden:NO];
    [self.view addSubview:myButtons];
    

    Here is the example class:

    MyFlexibleSpacedButtons.h:

    #import <UIKit/UIKit.h>
    
    @interface MyFlexibleSpacedButtons : UIView
    
    @property (nonatomic, readonly) NSArray *allButtons;
    
    - (void)setButtonAtIndex:(NSUInteger)index hidden:(BOOL)hidden;
    
    @end
    

    MyFlexibleSpacedButtons.m:

    #import "MyFlexibleSpacedButtons.h"
    
    const NSUInteger    maxButtons          = 9;
    const NSUInteger    buttonsPerRow       = 3; // This can not be 0.
    const CGFloat       buttonHeight        = 50;
    const CGFloat       buttonWidth         = 50;
    const CGFloat       spaceBetweenButtons = 10.0;
    
    @interface MyFlexibleSpacedButtons ()
    @property (nonatomic, readwrite) NSArray        *allButtons;
    @end
    
    @implementation MyFlexibleSpacedButtons
    
    @synthesize allButtons;
    
    - (id)initWithFrame:(CGRect)frame
    {
        NSUInteger numberOfRows = ceil((double)maxButtons / (double)buttonsPerRow);
        CGFloat    minHeight    = buttonHeight * numberOfRows + spaceBetweenButtons * (numberOfRows - 1);
        if (frame.size.height < minHeight)
        {
            frame.size.height = minHeight;
        }
    
        CGFloat minWidth = buttonWidth * maxButtons + spaceBetweenButtons * (maxButtons-1);
        if (frame.size.width < minWidth)
        {
            frame.size.width = minWidth;
        }
    
        self = [super initWithFrame:frame];
        if (self) {
            // Defaults
            // Uncomment the following line if needed for debugging:
            // self.backgroundColor = [UIColor grayColor];
    
            // Create the buttons and add them to the array.  Default to hidden buttons
            self.allButtons = [NSArray new];
            for (int i = 0; i < maxButtons; i++)
            {
                UIButton *button   = [UIButton buttonWithType:UIButtonTypeRoundedRect];
                button.hidden      = YES;
                [button setTitle:[NSString stringWithFormat:@"%d", i] forState:UIControlStateNormal];
                [self addSubview:button];
                self.allButtons = [self.allButtons arrayByAddingObject:button];
            }
    
            [self setButtonFrames];
        }
        return self;
    }
    
    - (void)dealloc
    {
        allButtons = nil;
    }
    
    - (void)setButtonFrames
    {
        CGFloat viewHeight                  = self.bounds.size.height;
        CGFloat viewWidth                   = self.bounds.size.width;
    
        NSUInteger  buttonCount             = [self visibleButtonsCount];
        NSUInteger  numberOfRows            = ceil((double)maxButtons / (double)buttonsPerRow);
        CGFloat     buttonY                 = (viewHeight - buttonHeight * numberOfRows - spaceBetweenButtons * (numberOfRows - 1)) / 2;
        CGFloat     buttonGroupTotalWidth   = buttonCount * buttonWidth + (buttonCount - 1) * spaceBetweenButtons;
        CGFloat     buttonGroupStartingX    = (viewWidth - buttonGroupTotalWidth) / 2;
    
        // Set frames of buttons
        NSUInteger  visibleButtonIndex      = 0;
        for (int i = 0; i < maxButtons; i++)
        {
            UIButton *button    = [self.allButtons objectAtIndex:i];
    
            if (!button.hidden)
            {
                CGFloat buttonX     = buttonGroupStartingX + visibleButtonIndex % buttonsPerRow * (buttonWidth + spaceBetweenButtons);
                button.frame        = CGRectMake(buttonX, buttonY, buttonWidth, buttonHeight);
                visibleButtonIndex++;
    
                if (visibleButtonIndex % buttonsPerRow == 0)
                {
                    buttonY = buttonY + buttonHeight + spaceBetweenButtons;
                }
            }
        }
    }
    
    - (void)setButtonAtIndex:(NSUInteger)index hidden:(BOOL)hidden
    {
        if (index > maxButtons - 1)
        {
            return;
        }
    
        UIButton *button    = [self.allButtons objectAtIndex:index];
        button.hidden       = hidden;
    
        [self setButtonFrames];
    }
    
    - (NSUInteger)visibleButtonsCount
    {
        NSUInteger buttonCount = 0;
    
        for (UIButton *button in self.allButtons)
        {
            if (!button.hidden)
            {
                buttonCount++;
            }
        }
        return buttonCount;
    }
    
    @end