Programmatically linked buttons - ios

I am designing an app that requires me to have a grid of buttons each with four different states. Unselected, Selected, Hit, or Miss. I am trying to generate these buttons programatically but I am having trouble having just one of them remain selected. Is there a way that maybe I could put them into an array and then iterate over the array to unselect them? I looked into IBOutletCollections but those will not work because I want to create these buttons programmatically

I think maybe you should check this demo I've learned in Developing iOS 7 Apps for iPhone and iPad cs193p.
https://github.com/elilien/Matchismo

This worked for me where _lastSelectedButton is the id of the last selected button and the width is 25
-(void)unselect
{
if(_lastSelectedButton){
//Unselect the last selected button However only change its background if is blue
[[_lastSelectedButton layer] setBorderWidth:0.0f];
if([_lastSelectedButton.titleLabel.text isEqual: #"0"]){
[[_lastSelectedButton layer] setBackgroundColor:[missColor CGColor]];
}else if([_lastSelectedButton.titleLabel.text isEqual: #"/"]){
[[_lastSelectedButton layer] setBackgroundColor: [hitColor CGColor]];
}
else{
[[_lastSelectedButton layer] setBackgroundColor:unselectedColor.CGColor];
}
}
_lastSelectedButton = nil;
}
- (void)advanceSelection:(UIButton*)sender
{
int i = 0;
//Find the indice that the present selected thing is
for( UIButton *button in btnList )
{
if( sender == button )
{
if((i >= (self.numberOfPlayers - 1) * width) &&(( i + 1) % 5 == 0)){
//Then animated scroll to the next frame
if (i + 1 == self.numberOfPlayers * width) {
//then it is the last button
[self unselect];
}else{
CGFloat x = (([_pageControl currentPage] + 1.0 ) * 320.0);
[_scrollView setContentOffset:CGPointMake(x, 0.0f) animated:YES];
[self selection:btnList[i - (width * (self.numberOfPlayers - 1)) + 1]];
[self scrollViewDidEndDecelerating:self.scrollView];
self.pageControl.currentPage = _pageControl.currentPage + 1;
}
}else if(i >= ((self.numberOfPlayers - 1) * width)){
//We want to move it up one
//subtract width times hieght and ad one
[self selection:btnList[i - (width * (self.numberOfPlayers - 1)) + 1]];
}else{
//To go one down add 25 (the width)
[self selection:btnList[i + 25]];
}
}
i++;
}
}

Related

Hide first view while current view is the last indexed view in icarousel

I want my carousel to be like this (as per image). When my current index will be 3 (last index), hide the first indexed View and when my current index will be 0 (first index), hide the last index. It should be cyclic, like a book .
Please help me out. I am trying this for 3 days and not getting a solution.
- (void)carouselCurrentItemIndexDidChange:(__unused iCarousel *)carousel{
[self updateCarousalAtIndex:(long)self.carousalView.currentItemIndex];
}
-(void)updateCarousalAtIndex:(NSInteger )index{
UIView *firstCard = (UIView *)[self.carousalView itemViewAtIndex:0];
UIView *lastCard = (UIView *)[self.carousalView itemViewAtIndex:(long)self.cardData.count-1];
if (index == 0) {
NSLog(#"updateCarousalAtIndex 0");
lastCard.alpha = 0.f;
firstCard.alpha = 1.f;
CGRect frame = lastCard.frame;
frame.size.width = 0.f;
lastCard.frame = frame;
}else{
NSLog(#"updateCarousalAtIndex %ld",(long)index);
firstCard.alpha = 0.f;
lastCard.alpha = 1.f;
CGRect frame = firstCard.frame;
frame.size.width = 0.f;
firstCard.frame = frame;
}
}
By adding the below code ,it will solve the problem of hiding back card
- (CGFloat)carousel:(iCarousel *)carousel valueForOption:(iCarouselOption)option withDefault:(CGFloat)value{
case iCarouselOptionFadeMin:
return 0;//for hiding first card
case iCarouselOptionFadeMax:
return 1;
case iCarouselOptionFadeRange:
return 3;
default: {
return value;
}
}

how to move one direction in UIScrollView when scrolling

I'm new in objective-c. I create UIScrollView object and add in my view with this code:
height = self.view.frame.size.height;
width = self.view.frame.size.width;
scrollbar = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
scrollbar.delegate = self;
scrollbar.backgroundColor = [UIColor whiteColor];
scrollbar.maximumZoomScale = 1.0;
scrollbar.minimumZoomScale = 1.0;
scrollbar.clipsToBounds = YES;
scrollbar.showsHorizontalScrollIndicator = YES;
scrollbar.pagingEnabled = YES;
[scrollbar setContentSize:CGSizeMake(width*4, height*4)];
[self.view addSubview:scrollbar];
for (int i = 1; i <= 4; i++) {
first = [[FirstViewController alloc]initWithNibName:#"FirstViewController" bundle:nil];
first.view.frame = CGRectMake((i-1)*width, 0, width, height*4);
[scrollbar addSubview:first.view];
switch (i) {
ase 1:
first.view.backgroundColor = [UIColor blueColor];
break;
case 2:
first.view.backgroundColor = [UIColor redColor];
break;
case 3:
first.view.backgroundColor = [UIColor greenColor];
break;
case 4:
first.view.backgroundColor = [UIColor grayColor];
break;
default:
break;
}
}
in my code I add 4 view in my ScrollView with different color now I want when scrolling on my ScrollView detect dx & dy (dx: driving distance on Axis.x & dy:driving distance on Axis.y) and check these two variable and when :
Notic: I want when any one touch on ScrollView and moving touch on Axis (x or y) or touch on Both Axis (x and y) check this :
if (dx > dy) disable horizontal scroll and moving in vertical side!!!
else moving in horizontal side and disable vertical scroll!!!
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGRect visibleRect = CGRectMake(scrollView.contentOffset.x, scrollView.contentOffset.y, scrollView.contentOffset.x + scrollView.bounds.size.width, scrollView.contentOffset.y + scrollView.bounds.size.height);
NSLog(#"%f,%f",visibleRect.origin.x,visibleRect.origin.y);
/*NSLog(#"x : %f",scrollView.contentOffset.x);
NSLog(#"y : %f",scrollView.contentOffset.y);*/
if (fabsf(scrollView.contentOffset.x) > fabsf(scrollView.contentOffset.y)) {
NSLog(#"Vertical Side");
}
else {
NSLog(#"Horizontal Side");
}
}
please guide me guys. I can't disable one side and move another side!!! thanks
If i understood you right, you want to disable scrolling in the direction that has been dragged less by the user. If so, UIScrollView already offers an option to do so:
scrollView.directionalLockEnabled = YES;
When this option is set to true UIScrollView will handle the correct direction lock by itself.
For more information look at the documentation: link
You can achieve the result you want adding some code to your delegate. This is my ViewController.m file. -viewDidLoad, #import statements and the other methods are omitted.
#interface ViewController () {
CGPoint initialOffset;
NSInteger direction; // 0 undefined, 1 horizontal, 2 vertical
}
#end
#implementation ViewController
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// retrieve current offset
CGPoint currentOffset = scrollView.contentOffset;
// do we know which is the predominant direction?
if (direction == 0) {
// no.
CGFloat dx = currentOffset.x - initialOffset.x;
CGFloat dy = currentOffset.y - initialOffset.y;
// we need to decide in which direction we are moving
if (fabs(dx) >= fabs(dy)) {
direction = 1; // horizontal
} else if (fabs(dy) > fabs(dx)) {
direction = 2;
}
}
// ok now we have the direction. update the offset if necessary
if (direction == 1 && currentOffset.y != initialOffset.y) {
// remove y offset
currentOffset.y = initialOffset.y;
// update
[scrollView setContentOffset:currentOffset];
} else if (direction == 2 && currentOffset.x != initialOffset.x) {
currentOffset.x = initialOffset.x;
[scrollView setContentOffset:currentOffset];
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
// store the current offset
initialOffset = scrollView.contentOffset;
// reset flag
direction = 0; // AKA undefined
}
#end
When you start dragging, the delegate will reset the flag direction to "unknown" state, and store the current content offset. After every dragging move, your -scrollViewDidScroll: will be called. There, you decide which is the predominant direction (if this hasn't been done yet) and correct the current scrolling offset accordingly, by removing the x (or y) offset.
I tested this with the same settings you provided, only I used a UIImageView inside UIScrollView and I set up everything via InterfaceBuilder, but it should work fine. Theoretically, with this method you could replace directionLock, but remember that -scrollViewDidScroll is called many times during an action, and every time it rewrites the content offset (if the scrolling is happening in both directions). So if you leave directionLock enabled, you save many of the calls to setContentOffset: that the delegate performs.

Adding a UIButton to UITableView but can't scroll down to the button

I've got a UITableView and I want to add a button just underneath the table. The table is bigger than the screen so to get to the button, I need to scroll down to it. The problem is, the screen will scroll to just above where the button is. So I can drag the screen slightly and the button is visible but when I let go, the screen springs back and I can't get to the button.
The button is set up as follows
for(int i = 0; i < [buttonArray count]; i++)
{
UIButton *but = [buttonArray objectAtIndex:i];
float xPosition = but.frame.origin.x;
float xGap = (self.view.frame.size.width - (but.frame.size.width)*2)/3;
if ((i % 2 == 0) && (i < [buttonArray count] - 1))
{
xPosition = xGap;
}
else if ((i % 2 == 1) && (i < [buttonArray count] - 1))
{
xPosition = (xGap * 2) + but.frame.size.width;
}
else
{
xPosition = (self.view.frame.size.width - but.frame.size.width)/2;
}
//Add space between row of buttons
if(i > 0)
{
if(i % 2 == 0)
{
yPosition+=but.frame.size.height;
CGRect screenRect = [[tableView superview] bounds];
//Add extra space between row of buttons
float fGap = (screenRect.size.height / 100.0) * 4.5;
yPosition+=fGap;
}
}
CGRect rect = CGRectMake(xPosition, yPosition, but.frame.size.width, but.frame.size.height);
but.frame = rect;
[but addTarget:self action:#selector(genericMethod:) forControlEvents:UIControlEventTouchUpInside];
CGRect screenRect = [self.view bounds];
CGFloat fHeight = screenRect.size.height;
float fMargin = (fHeight / 100.0) * 0.8;
but.titleEdgeInsets = UIEdgeInsetsMake(0, 0, fMargin, 0);
[tableView addSubview:but];
}
}
So I go through an array of buttons (only one button in the array in my current case) and set up the frames and position them into columns and add it as a subview to the tableView. Things I've tried are:
Increase the table footer size. This just makes the gap between the table and button increase and I still can't scroll down as far as the button.
Add the button to the footer. This kind of worked. I could get to the button, but the button was always on screen, not just static below the table.
Add the button to a separate view below the table and add that view to the viewController.view. This didn't work at all and the button wasn't displayed.
Set the tableView.tableFooterView equal to the button. This also kind of worked and I could reach the button but it wouldn't work when there is more than one button in the array above.
You should create a custom view. Inside that view you should add all the buttons created using buttonArray. Now, assign this custom view to tableFooterView.
UIView *customView = [[UIView alloc] initWithFrame:properFrame];
for(int i = 0; i < [buttonArray count]; i++)
{
//...calculation of frame and other stuff
[customView addSubview:but];
}
tableView.tableFooterView = customView;
This should solve your scrolling issue.
If you want to make bottom view as stable,Take a UIView at the bottom
of UITableView.
If you will use TableFooterView,It will be at the end of cell .

getting the contents of UIButton press

I'm not sure if this has been asked before, but I'm gonna ask anyways. I am working on a grid of buttons, with letters as the titles of each one. I am getting the contents of these letters from an array, and I believe that is where my issue is coming from. The titles appear just fine, but when I press on any of the buttons in my array, that is where my issue comes in.
Every time I press a letter, the part of my NSLog NSLog(#"Letter Added to Array: %#", self.sectionButton.titleLabel.text);
displays
Letter Added to Array: S
and that is all that is displayed. No matter what button is pushed. I am thinking it might just be because of S being the last object in my array, which is why it's saying that. I don't know, so ANY help would be appreciated.
Here is my code:
- (void) didTouchButton
{
[self.lettersPressedArray addObject:self.sectionButton.titleLabel.text];
NSLog(#"Letter Added to Array: %#", self.sectionButton.titleLabel.text);
NSLog(#"Letters Pressed = %#", self.lettersPressedArray);
}
- (void)showGridWithRows:(int)r columns:(int)c arrayOfContent:(NSArray *)content withSizeOfContent:(CGFloat)contentSize
{
for (int i = 0; i < content.count; i++) {
// vars
int row = (int)i / r; // to figure out the rows
int col = i % c; // to figure out the columns
float x = 0;
float y = 0;
// sizing options
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
CGSize screen = [UIScreen mainScreen].bounds.size;
if (screen.height == 480) {
x = 1 + (40 * col);
y = 1 + (30 * row);
}
else {
x = 2 + (40 * col);
y = 1 + (31 * row);
}
}
else {
x = .5 + (90 * col);
y = 1 + (90 * row);
}
//button
self.sectionButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.sectionButton setTintColor:[UIColor lightGrayColor]];
self.sectionButton.frame = CGRectMake(x, y, contentSize, contentSize);
self.sectionButton.tag = 100 + row * c + col;
[self.sectionButton addTarget:self action:#selector(didTouchButton) forControlEvents:UIControlEventTouchUpInside];
// font stuff
self.sectionButton.titleLabel.textAlignment = NSTextAlignmentCenter;
self.sectionButton.titleLabel.backgroundColor = [UIColor clearColor];
self.sectionButton.titleLabel.font = [UIFont fontWithName:#"Helvetica-Light" size:22];
[self.sectionButton setTitleColor:[UIColor blackColor]/*[UIColor colorWithRed:241.0/255.0 green:124.0/255.0 blue:22.0/255.0 alpha:1.0]*/ forState:UIControlStateNormal];
// title
s = (NSString *)[content objectAtIndex:i];
[self.sectionButton setTitle:s forState:UIControlStateNormal];
// color
if ([s isEqualToString:#"A"] ) {
[self.sectionButton setBackgroundColor:[UIColor greenColor]];
}
else if([s isEqualToString:#"Z"]) {
[self.sectionButton setBackgroundColor:[UIColor redColor]];
}
else {
[self.sectionButton setBackgroundColor:[UIColor lightGrayColor]];
}
//layer
self.sectionButton.layer.borderWidth = .65;
//self.sectionButton.layer.cornerRadius = 5.0;
for(int j = 0; j < c; j++) {
for (int k = 0; k < r; k++) {
[self addSubview:self.sectionButton];
}
}
}
}
Change this:
- (void) didTouchButton
{
[self.lettersPressedArray addObject:self.sectionButton.titleLabel.text];
NSLog(#"Letter Added to Array: %#", self.sectionButton.titleLabel.text);
NSLog(#"Letters Pressed = %#", self.lettersPressedArray);
}
To this:
- (void) didTouchButton:(UIButton *)sender
{
[self.lettersPressedArray addObject:sender.titleLabel.text];
NSLog(#"Letter Added to Array: %#", sender.titleLabel.text);
NSLog(#"Letters Pressed = %#", self.lettersPressedArray);
}
And when you assign your button selector add a ':'
So, this:
#selector(didTouchButton)
Will be:
#selector(didTouchButton:)
Probably implemented like:
[someButton addTarget:self action:#selector(didTouchButton:) forControlEvents:UIControlEventTouchUpInside];
Because:
The way you originally reference your button via self.selectionButton.titleLabel.text accesses one specific instance of a button. This means that no matter what button triggers didTouchButton it gets the content of self.selectionButton which I'm assuming displays an "S". This means that no matter what happens, you add more of the letter "S" to your array. By configuring our action method like we did, it will pass "self" as an argument. This means that we have a variable representing whoever called the method within the method. We will use that to get our contents and add them to the array.
Couple things here. Firstly, you likely don't want to have the button be a property, because you are constantly overwriting it and are ultimately left with it referencing the last one you created... What you're basically doing is the same as:
int x = 1;
x = 2;
x = 3;
printing x will ALWAYS result in 3... Make sense?
The solution to your problem is to pass the button you are tapping as a parameter to the function that handles the action, by adding in a ":" after "didTouchButton" and changing the way you create that function. When you create the button, add the : after the function name like this:
[button addTarget:self action:#selector(didTouchButton:) forControlEvents:UIControlEventTouchUpInside];
That allows a reference to the button pressed to be passed to the function, so you can do this to handle it:
- (void)didTouchButton:(UIButton *)button {
NSString *titleOfPressedButton = button.titleLabel.text;
}
You need to change didTouchButton method to accept (id)sender parameter and change how you set up it's selector while creating a button to didTouchButton:.
This way you will receive a button object pointer inside the didTouchButton and will be able to get its information.

Best way to auto arrange grid style elements on rotation?

I have a grid of items in my application like so:
http://cl.ly/1m243117220B060Z0M26/Screen%20Shot%202012-07-09%20at%2011.34.22%20AM.png
These are just custom UIButtons at the moment. What might be the best way to go about making these grid items rearrange automatically to fit the width of landscape mode?
http://cl.ly/1d0x431P1n211W1H2n3B/Screen%20Shot%202012-07-09%20at%2011.35.42%20AM.png
Obviously this is done commonly in apps but I searched a bit and couldn't find exactly what I was looking for.
Added bonus if someone knows of something like Isotope for iOS (http://isotope.metafizzy.co/)
You probably want a routine that adjusts your scrollview and the images (or in my case, buttons with images) based upon the size of the scrollview (and make sure you set the scrollview's autoSizingMask so it stretches as the orientation changes).
So, for example, I have a routine that does the following (it assumes you have a scrollview created with UIButton's already added for each of your icons ... this basic idea would work if you're using UIImageViews, too, though):
- (void)rearrangeImages
{
if (!_listOfImages)
{
[self loadImages];
return;
}
// a few varibles to keep track of where I am
int const imageWidth = [self thumbnailSize];
int const imagesPerRow = self.view.frame.size.width / (imageWidth + 2);
int const imageHeight = imageWidth;
int const imagePadding = (self.view.frame.size.width - imageWidth*imagesPerRow) / (imagesPerRow + 1);
int const cellWidth = imageWidth + imagePadding;
int const cellHeight = imageHeight + imagePadding;
NSInteger row;
NSInteger column;
NSInteger index;
CGRect newFrame;
// iterate through the buttons
for (UIView *button in [_scrollView subviews])
{
index = [button tag];
if ([button isKindOfClass:[UIButton class]] && index < [_listOfImages count])
{
// figure out where the button should go
row = floor(index / imagesPerRow);
column = index % imagesPerRow;
newFrame = CGRectMake(column * cellWidth + imagePadding,
row * cellHeight,
imageWidth,
imageHeight);
if (button.frame.origin.x != newFrame.origin.x || button.frame.origin.y != newFrame.origin.y)
[button setFrame:newFrame];
}
}
NSInteger numberOfRows = floor(([_listOfImages count] - 1) / imagesPerRow) + 1;
[_scrollView setContentSize:CGSizeMake(self.view.frame.size.width, numberOfRows * cellHeight)];
}
I then have my app call this when the screen changes orientation, e.g.,
- (void)viewWillLayoutSubviews
{
[self rearrangeImages];
}
If you're supporting pre iOS 5, you might also need something like:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
float iOSversion = [[[UIDevice currentDevice] systemVersion] floatValue];
// we don't need to do this in iOS 5, because viewWillLayoutSubviews is automatically called
if (iOSversion < 5.0)
[self viewWillLayoutSubviews];
}

Resources