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.
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!
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
I think the easiest and more flexible way would be to use an UITableView and a custom cell with a method: - (void)NumberOfButtonsInCell:(NSUInteger)buttons;
so when called in the tableView data source
you just set the number of buttons accordingly as
- (void)numberOfButtonsInCell:(NSUInteger)buttons {
int i;
for (i = 0; i<buttons;i++) {
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake((cell.frame.size.widht/buttons)*i, 0, buttonWidth, buttonHeight)];
button.tag = i;
[self addSubview:button];
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
}
}
- (void)buttonPressed:(id)sender {
UIButton *button = (UIButton*)sender;
[self.delegate userDidSelectButtonAtIndex:(button.tag)];
//so your table view resolves the row with [self.tableView indexPathForCell:cell];
}
That way you can set a method, for [cell configureButtonAtIndex:index];
You could easily change all the behavior, the looks, and even the design, with a few lines of code.
Related
What would be the best way on iOS to achieve drawing a grid of circles with fixed columns and rows ?. I would want to identify each circle when the user taps on it.
I tried using collection views but it seems CoreGraphics is meant for tasks like these.
You can create buttons dynamically as follows :
- (void)drawSheet
{
int numberOfRow=5;
int numberOfColumn=4;
float x = 1,
y = 20,
padding = 5,
width = (self.view.frame.size.width-(padding*(numberOfColumn-1)))/numberOfColumn,
height = (self.view.frame.size.height-(padding*(numberOfRow-1)))/numberOfRow;
int counter = 0;
for (int i=0; i<numberOfRow; i++) {
for (int j=0; j<numberOfColumn; j++) {
UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(x, y, 74, 74)];
btn.layer.cornerRadius = btn.frame.size.width/2;
btn.layer.borderColor = [UIColor grayColor].CGColor;
btn.layer.borderWidth = 2.0;
[btn addTarget:self action:#selector(buttonPress:) forControlEvents:UIControlEventTouchUpInside];
btn.clipsToBounds = YES;
btn.tag = counter;
[self.view addSubview:btn];
x = x + width + padding;
counter = counter + 1;
}
x = 0;
y = y + height + padding;
}
}
When you tap on it, you will get tag of it :
- (IBAction)buttonPress:(UIButton *)sender{
NSLog(#"%ld",sender.tag);
}
hi Please check the below code. i have added 3 circles in a row, you could use more if you want.
- (void)viewDidLoad {
[super viewDidLoad];
UIView *circleView;
int x = 10;
int y = 20;
static int tagNum = 0;
for (int row=0; row < 5;) {
for (int cirNum =0 ; cirNum <3;) {
circleView = [[UIView alloc] initWithFrame:CGRectMake(x,y,100,100)];
circleView.alpha = 0.5;
circleView.layer.cornerRadius = 50;
circleView.backgroundColor = [UIColor blueColor];
circleView.tag = tagNum;
NSLog(#"tagNum is %d", tagNum);
[self.view addSubview:circleView];
cirNum ++;
x = x + 100;
tagNum ++;
//y = y + 20;
}
row++;
y = y + 100;
x = 10;
}
// Do any additional setup after loading the view, typically from a nib.
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint location = [[touches anyObject] locationInView:self.view];
CGRect fingerRect = CGRectMake(location.x-5, location.y-5, 10, 10);
for(UIView *view in self.view.subviews){
CGRect subviewFrame = view.frame;
if(CGRectIntersectsRect(fingerRect, subviewFrame)){
//we found the finally touched view
//NSLog(#"Yeah !, i found it %#",view);
NSLog(#"view tag touched is %ld",view.tag);
}
}
}
output would look like this
I have a scrollView with many buttons on it. I'm trying to get the Button position form the scroll so I can scroll it to that position. My issue is I always get the same value in _btnV.frame.origin.x once I click on each button. So please how I can resolve this issue?
- (void)scrollView{
segmentWidth = screenWidth / 5;
NSArray *images = #[#"Smiley-1.png",#"Smiley-2.png",#"Smiley-3.png",#"Smiley-4.png",#"Smiley-5.png",#"Smiley-5.png",#"Smiley-5.png",#"Smiley-4.png",#"Smiley-1.png",#"Smiley-2.png",#"Smiley-3.png",#"Smiley-4.png"];
long ScrollContentSize = ((images.count -1)*segmentWidth)+screenWidth;
_scrlView.contentSize = CGSizeMake(ScrollContentSize, _scrlView.frame.size.height);
float screenX2 = screenHeight/2 - segmentWidth/2;
__block float screenX = (screenWidth-segmentWidth)/2;
NSMutableArray *arr = [[NSMutableArray alloc] init];
for(int i=0; i<images.count; i++)
{
_btnV = [[UIButton alloc]init];
[_btnV setFrame:CGRectMake(screenX, screenX2, segmentWidth, segmentWidth)];
[_btnV setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%#",[images objectAtIndex:i]]] forState:UIControlStateNormal];
[_btnV setBackgroundColor:[UIColor redColor]];
[_btnV addTarget:self action:#selector(scrollToMe:) forControlEvents:UIControlEventTouchUpInside];
[_btnV setTag:i];
[arr addObject:_btnV];
[_scrlView addSubview:_btnV];
screenX = screenX+segmentWidth;
}
_menuItems = arr;
[self.view addSubview:_scrlView];
}
-(void) scrollToMe:(id)sender{
int scrollX = _scrlView.contentOffset.x;
int mod = scrollX % segmentWidth;
int position = scrollX/segmentWidth;
float positionOffset = (float) mod / segmentWidth;
scrollX = position * segmentWidth;
NSLog(#"position:%f",_btnV.frame.origin.x);
//scrollX = ...
[_scrlView setContentOffset:CGPointMake(scrollX, scrollX) animated:YES];
}
Find the button using its tag value
-(void) scrollTo:(id)sender{
UIButton * button = (UIButton *)sender;
int position = button.tag;
scrollX = position * segmentWidth;
NSLog(#"position:%f",_btnV.frame.origin.x);
//scrollX = ...
[_scrlView setContentOffset:CGPointMake(scrollX, scrollX) animated:YES];
}
You are calling selector method of UIButton. so in this method, you can directly get UIButton position with respect to UIScrollView. like
-(void) scrollTo:(id)sender{
UIButton* btnClick = (UIButton*)sender;
NSLog(#"X position : %f",btnClick.frame.origin.x);
}
Hope this help you.
try this , it will log position of all your button correctly
-(void) scrollTo:(id)sender{
int scrollX = _scrlView.contentOffset.x;
int mod = scrollX % segmentWidth;
int position = scrollX/segmentWidth;
float positionOffset = (float) mod / segmentWidth;
scrollX = position * segmentWidth;
for(int i=0; i<images.count; i++)
{ if([[[_scrlView subviews] objectAtIndex:i] isKindOfClass:[UIButton class]]) {
Button btn = [[_scrlView subviews] objectAtIndex:i];
NSLog(#"position:%f",btn.frame.origin.x);
}
}
//scrollX = ...
[_scrlView setContentOffset:CGPointMake(scrollX, scrollX) animated:YES];
}
I am banging my head on this but cannot figure out the issue.
I have a custom UIView: FKPoiMiniDetail and this is the implementation (FKPoiMiniDetail.m)
const static CGFloat kDialogLateralMargin = 15.0;
const static CGFloat kDialogHeight = 100.0;
const static CGFloat kDialogBottomMargin = 10.0;
const static CGFloat kBottomButtonHeight = 50.0;
#interface FKPoiMiniDetail ()
#property (nonatomic) float tabBarHeight;
#property (nonatomic) NSUInteger numberOfButtons;
#property (nonatomic) NSMutableArray *buttonBlocks;
#property (strong, nonatomic) PoI* myPoi;
#end
#implementation FKPoiMiniDetail
- (id) initWithTabBarHeight:(float)aTabBarHeight numberOfButtons:(NSUInteger)numButtons andPoi:(PoI*)aPoi{
if (self = [super init]) {
self.tabBarHeight = aTabBarHeight;
self.numberOfButtons = numButtons;
self.myPoi = aPoi;
self.buttonBlocks = [[NSMutableArray alloc] init];
[self configure];
}
return self;
}
and here I add the the buttons programmatically:
- (void) assignButton:(NSUInteger)index anImage:(UIImage*)image aText:(NSString*)text andCallback:(void(^)())cb{
if (index > self.numberOfButtons) {
ALog("ERROR: Index is greater than number of buttons");
return;
}
[self.buttonBlocks addObject:cb];
int imWidth = 20;
int imHeight = 20;
int imLabelSpacing = 8;
CGSize labelSize = [text sizeWithAttributes:#{NSFontAttributeName: [UIFont systemFontOfSize:17.0f]}];
CGFloat overallWidth = labelSize.width + imWidth + imLabelSpacing;
CGFloat test = self.bounds.size.width;
CGFloat buttonWidth = test / ((CGFloat) self.numberOfButtons);
int x = index * buttonWidth;
int y = self.frame.size.height - kBottomButtonHeight;
UIButton *buttonContainer = [[UIButton alloc] initWithFrame:CGRectMake(x,y,buttonWidth,kBottomButtonHeight)];
[buttonContainer addTarget:self action:#selector(buttonTouched:) forControlEvents:UIControlEventTouchUpInside];
buttonContainer.tag = index;
int firstImOrigin = x + (buttonWidth - overallWidth) / 2;
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(firstImOrigin, (kBottomButtonHeight - imHeight)/2, imWidth, imHeight)];
[iv setImage:image];
[buttonContainer addSubview:iv];
CGRect lR = CGRectMake(firstImOrigin + imWidth + imLabelSpacing, (kBottomButtonHeight - labelSize.height)/2, labelSize.width, labelSize.height);
UILabel *label = [[UILabel alloc] initWithFrame:lR];
label.text = text;
[buttonContainer addSubview:label];
[self addSubview:buttonContainer];
}
and this is the selector as the target of the UIButton:
- (void)buttonTouched:(UIButton*)b{
void (^ myblock)() = [self.buttonBlocks objectAtIndex:b.tag];
myblock();
}
Somewhere else I create this object and assign buttons like this:
FKPoiMiniDetail *md = [[FKPoiMiniDetail alloc] initWithTabBarHeight:self.tabBarController.tabBar.frame.size.height
numberOfButtons:2
andPoi:annotation.aPoI];
UIImage *im = [UIImage imageNamed:#"detail.png"];
[md assignButton:0 anImage:im aText:#"Dettagli" andCallback:^{
ALog("Button 0 pressed");
}];
[md assignButton:1 anImage:im aText:#"Naviga" andCallback:^{
ALog("Button 1 pressed");
}];
[self.mapView addSubview:md];
The second button is empty, but the strange thing is this: clicking inside the button the callback does execute...
Debugging seems useless:
The variables for the first button:
and the variables for the second button:
Any help is appreciated, thanks in advance!
"Decomposing" your FKPoiMiniDetail
FKPoiMiniDetail
View
buttonContainer1 - buttonContainer2 — buttonContainer3, etc
So what's inside a buttonContainer should always have the same "frames" whereas it's buttonContainer frame that changes, according to the number of buttons, since buttonContainer will be their superview.
So you declare:
int x = index * buttonWidth;
int y = self.frame.size.height - kBottomButtonHeight;
The'll help setting the buttonContainer frame.
But label and iv should not depend of them, only depend of the height and the width of [buttonContainer frame] since they seems to be centered.
I guess that from you code, if you do at the end [self addSubview:iv] and [self addSubview:label]; you may see them.
I've had problems with setting images on UIImageView in the past and my solution has been changing the order to this:
[buttonContainer addSubview:iv];
[iv setImage:image];
First adding he subview and then setting the image.
I want to create a scroll view like Fleck app (https://itunes.apple.com/us/app/fleck-the-bigger-picture/id619977675). When you scroll items change his height. One increases the other decreases
I created this. But this work queerly. Perhaps there are similar elements. They could help me...
#interface FleckView()
#property (strong, nonatomic) NSMutableArray* items;
#property (strong, nonatomic) NSMutableArray* cells;
#property (assign, nonatomic) CGFloat old_y;
#property (assign, nonatomic) CGFloat step;
#end
#implementation FleckView
#define kCellHeight 80.0
#define kCellHeightActive 320.0
int counter = 0;
float diff = (kCellHeightActive - kCellHeight) / kCellHeight; // 3.5
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(void) loadItems: (NSMutableArray*) items{
self.old_y = 0;
self.step = 0;
self.delegate = self;
self.cells = [[NSMutableArray alloc] init];
self.items = items;
self.contentSize = CGSizeMake(320, kCellHeightActive + (self.items.count) * kCellHeight);
//add first
UIView *view = [self.items objectAtIndex:0];
view.frame = CGRectMake(0, 0, 320, kCellHeightActive);
[self.cells addObject: view];
[self addSubview:view];
//add other
for(int i = 1; i < self.items.count; i++){
UIView *view = [self.items objectAtIndex:i];
view.frame = CGRectMake(0, ((i - 1) * kCellHeight) + kCellHeightActive, 320, kCellHeight);
[self.cells addObject: view];
[self addSubview:view];
}
}
////MAIN WORK
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
counter = (scrollView.contentOffset.y) / kCellHeight ;
self.step = scrollView.contentOffset.y - self.old_y;
self.old_y = scrollView.contentOffset.y;
if(counter < self.items.count){
[self setHeightRow: counter + 1];
}
}
-(void) setHeightRow: (int) rowNum{
UIView *cell_prev = [self.cells objectAtIndex: rowNum - 1];
UIView *cell = [self.cells objectAtIndex: rowNum];
CGFloat height = cell.frame.size.height;
height = height + (self.step * diff);
if(height < 90)
height = 80;
CGFloat height_prev = kCellHeightActive + kCellHeight - height;
NSLog(#"HEIGHT_PREV: %f", height_prev);
NSLog(#"HEIGHT: %f", height);
CGFloat y_prev = counter * kCellHeight;
CGFloat y = y_prev + height_prev;
NSLog(#"___Y_PREV: %f", y_prev);
NSLog(#"___Y: %f", y);
cell_prev.frame = CGRectMake(0, y_prev, 320, height_prev);
cell.frame = CGRectMake(0, y, 320, height);
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
float bottom = counter * kCellHeight;
float top = (counter + 1) * kCellHeight;
if(targetContentOffset->y > kCellHeight / 2 + counter * kCellHeight){
targetContentOffset->y = top;
}
else{
targetContentOffset->y = bottom;
}
}
I have a weird behavior with custom UIPageControl.
I subclassed it and in setCurrentPage method I draw custom dots.
In iOS 6 on load they just miss.
In iOS 7 the dots are misplaced.
In both iOS's dots are being positioned correctly only after I change the slide.
I tried the following methods without success:
[customPageControl updateCurrentPageDisplay];
[customPageControl updateConstraints];
[customPageControl sizeToFit];
[customPageControl.currentPage = 1];
The code:
#implementation ProfilePagesPageControl
#synthesize originalSubviews;
#synthesize dotsHeight;
- (void)setCurrentPage:(NSInteger)page
{
[super setCurrentPage:page];
[self updateDots];
}
- (void)updateDots
{
for (int i = 0; i < self.subviews.count; i++)
{
UIView* dotView = [self.subviews objectAtIndex:i];
for (int j = dotView.subviews.count - 1; j >= 0; j--)
{
UIView *view = [dotView.subviews objectAtIndex:j];
[view removeFromSuperview];
}
int widthForDots = [Utils getDeviceBounds].width - Constraints.Gap * 3;
int effectiveWidth = widthForDots / self.subviews.count - Constraints.Gap;
int effectiveGap = (widthForDots - (effectiveWidth * self.subviews.count) + Constraints.Gap * 3) / self.subviews.count;
int effectiveHeight = dotsHeight;
if (i == self.currentPage)
effectiveHeight = effectiveHeight + 3;
else
effectiveHeight = effectiveHeight - 3;
CGRect dotViewFrame = dotView.frame;
dotViewFrame.origin.x = effectiveWidth * i + Constraints.Gap + effectiveGap * i;
dotViewFrame.origin.y = self.frame.size.height / 2 - effectiveHeight / 2;
dotViewFrame.size.width = effectiveWidth;
dotViewFrame.size.height = effectiveHeight;
dotView.frame = dotViewFrame;
UIView *dotSubView = [[UIView alloc] initWithFrame:CGRectMake(0,
0,
dotView.frame.size.width,
dotView.frame.size.height)];
if (i == self.currentPage)
dotSubView.backgroundColor = [UIColor blackColor];
else
dotSubView.backgroundColor = [UIColor grayColor];
[UIUtils roundCorner:(UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight) forView:dotSubView];
ProfileSlideView *slideView = [self.originalSubviews objectAtIndex:i];
RevUILabel *label = [[RevUILabel alloc] initWithSize:13];
label.text = slideView.name;
label.frame = CGRectMake(dotSubView.frame.size.width / 2 - label.frame.size.width / 2,
dotSubView.frame.size.height / 2 - label.frame.size.height / 2,
label.frame.size.width,
label.frame.size.height);
[dotSubView addSubview:label];
[dotView addSubview:dotSubView];
}
}
#end
The iOS7 look:
The iOS6 look:
:
After slide look:
Make sure you call updateDots somewhere else (such as viewDidLoad, or viewWillAppear) so they can be set up before you switch any pages. If you only call updateDots in setCurrentPage it will only be called once you switch the page the first time.