How can I create an array of buttons in xcode? - ios

I have a view containing 80 UIButtons, and 80 UIImages. Rather than refer to these by individual outlet references, I would like to refer to them as indexes in an array, and so be able to change the Image, and work out which UIButton is sending a message without specific references.
I am sure this must be possible, as there is no way having 80 different versions of the same code is correct way to do this!
Is this possible?

You may be better served by looking into UICollectionView, but to answer the question as asked:
- (void)viewDidLoad {
[super viewDidLoad];
self.buttonArray = [NSMutableArray array];
for (int i = 0; i < 80; i = i + 1) {
// However you wish to get your button
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, i * 20, 20, 10);
[self.view addSubview:button];
// Other button-specific stuff (like setting the image, etc.)
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.buttonArray addObject:button];
}
}
- (void)buttonPressed:(UIButton *)sender {
int index = [self.buttonArray indexOfObject:sender];
// Now handle the button press based
}

It is possible, it’s called outlet collection.
#property(nonatomic,retain) IBOutletCollection NSArray *buttonsArray;

Related

iOS disable programatically created buttons until function completion

Hi and thanks in advance for you patience and help :)
Here is what I am doing:
- I create programmatically multiple buttons that call the same function
And what I want to accomplish:
- after a button is pressed, I want the common function to run and all the buttons to be disabled so that the function won't be called again during execution of the firs call. after the first call is ended I want to reenable the buttons.
Here is how I create the buttons:
#interface ViewController ()
{
UIButton *button;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
int tagForTheButton = 0;
int yPossition = 0;
int xPossition = 0;
for (int numberOfTheColomn = 0; numberOfTheColomn < 6; numberOfTheColomn++) {
button = [UIButton buttonWithType:UIButtonTypeCustom];
button.adjustsImageWhenHighlighted = NO;
button.frame = CGRectMake(xPossition, yPossition, 64, 64);
button.tag = tagForTheButton;
[button setTitle:title forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self.scroll addSubview:button];
yPossition=yPossition+100;
tagForTheButton++;
}
}
-(void)buttonPressed:(id)sender{
//This is my attempt to disable the buttons
//previous mistake used 2578
for (int numberForIncrease; numberForIncrease<6; numberForIncrease++) {
if (button.tag == numberForIncrease) {
UIButton *btn = (UIButton*)[self.scroll viewWithTag:numberForIncrease];
btn.enabled=NO;
[btn setUserInteractionEnabled:NO];
}
}
}
You must be disabling it incorrectly. The docs for UIButton.enabled state:
If the enabled state is NO, the control ignores touch events and
subclasses may draw differently.
Check that DisableButton is being called by adding an NSLog() call, & do away with the DisableButton method altogether:
dispatch_sync(dispatch_get_main_queue(), ^{
self.view.userInteractionEnabled = NO;
_btn_sync_outlet.enabled = NO;
_btn_sync_outlet.userInteractionEnabled = NO;
});
This might helps you :)
I don't understand why you need to loop from 0 to 2577 for disabling a button which you already have an iVar pointing to..?
why don't you just switch off userInteraction to your entire view (or if that is not possible to a containerView which holds all the buttons)?
[self.view setUserInteractionEnabled: NO ];
or, failing that why not just haver an iVar there?
{
BOOL _ignoreButtons;
}
-(void)buttonPressed:(id)sender{
if (!_ignoreButtons){
_ignoreButtons = YES;
//do your time consuming job, possibly on bg thread
//on completion..
_ignoreButtons = NO;
}
}

UIButton suddenly stops working

So I'm having issues with the buttons for the filters in my photo app. I'm not exactly sure why, but they've suddenly stopped working. The selector is not being called when they're tapped, but I have no idea why. They were working just fine a few days ago, but for some reason they're not now. I've checked the UIView subviews array, verified that it's right on top. I need a way to see if, for some reason, the touches are not making it to the button. I'm not sure how to do this.
I wish I had more information to give, but this is all I've got. I hope someone has some suggestions because I'm at wit's end with it.
Thanks in advance!
Button Creation Method:
-(void)createFilterButtonsForThumbnail:(UIImage *)thumbnail
{
UIView *filtersContainer = self.filterContainer;
CGRect buttonFrame = CGRectMake(kFilterFrameThickness, kFilterFrameThickness,
thumbnail.size.width, thumbnail.size.height);
CGFloat frameWidth = thumbnail.size.width+(2*kFilterFrameThickness);
CGFloat frameHeight = kFilterPickerHeight;
UIEdgeInsets backgroundInsets = UIEdgeInsetsMake(0, kFilterFrameThickness, 0, kFilterFrameThickness);
UIImage *buttonBackgroundImage = [[UIImage imageNamed:#"FilmReel"] resizableImageWithCapInsets:backgroundInsets];
for (int i = 0;i<(self.filterPaths.count+kFilterNonLookups+1);i++){
UIImageView *buttonBackground = [[UIImageView alloc] initWithFrame:CGRectMake(kFilterSidePadding+(i*(frameWidth+kFilterSidePadding)),
0,
frameWidth,
frameHeight)];
[buttonBackground setImage:buttonBackgroundImage];
UIButton *thumbnailButton = [[UIButton alloc] initWithFrame:buttonFrame];
UIImage *filteredThumbnail = [self applyFilterAtIndex:i ToImage:thumbnail];
[thumbnailButton setImage:filteredThumbnail forState:UIControlStateNormal];
[thumbnailButton addTarget:self action:#selector(filterSelected:) forControlEvents:UIControlEventTouchUpInside];
[thumbnailButton setTag:i];
[buttonBackground addSubview:thumbnailButton];
[filtersContainer addSubview:buttonBackground];
if ((i > (kFilterProMinimumIndex)) && ([self isProVersion]) == NO){
UIImageView *proTag = [[UIImageView alloc] initWithImage:nil];
CGRect proFrame = CGRectMake(buttonFrame.origin.x,
buttonFrame.origin.y + buttonFrame.size.height - kFilterProIconHeight-kFilterFrameThickness,
kFilterProIconWidth,
kFilterProIconHeight);
[proTag setBackgroundColor:[UIColor orangeColor]];
[proTag setFrame:proFrame];
[thumbnailButton addSubview:proTag];
[self.filterProTags addObject:proTag];
}
}
}
Selector Method:
-(void)filterSelected:(UIButton *)button
{
NSLog(#"Pressed button for index %i",button.tag);
int buttonTag = button.tag;
if ((buttonTag < kFilterProMinimumIndex+1) || ([self isProVersion] == YES)){
[self.imageView setImage:[self applyFilterAtIndex:buttonTag ToImage:self.workingImage]];
}
else {
[self processProPurchaseAfterAlert];
}
}
I see a couple issues in this, but I'm not sure which ones are causing it to break. First, you should instantiate your button using buttonWithType: on UIButton:
UIButton *thumbnailButton = [UIButton buttonWithType:UIButtonTypeCustom];
[thumbnailButton setFrame:buttonFrame]
The reason for this is UIButton is a class cluster, and you should let the OS give you the correct button.
Secondly, you're adding the Button as a Subview of an UIImageView, which by default, has userInteractionEnabled set to NO.
This property is inherited from the UIView parent class. This class
changes the default value of this property to NO.
You should have the button be one large button, with the background of the button be the image you want it to be.

iOS: How to add more views after viewDidLoad

I am new to IOS programming. I am starting from the basic application created by XCode 4.3 (SingleView application).
Continuing from my above question, I was able to add Views programmaticaly to the main ViewController in the Appdelegate but If I want to do the same after the view has loaded (inside viewDidLoad), I am facing the trouble. I tried using the same logic of adding subviews but I am not sure if the view needs a refresh after adding inside 'viewDidLoad'
My views are very simple, here is the code
-(void)viewDidLoad
{
[super viewDidLoad];
UIBUtton *btn = [UIBUtton] alloc] initWithFrame:CGRect(10,10,50,50)];
[self.view addSubView:btn];
[self.view setNeedsDsiplay];
}
Can someone please help?
You are not instantiating the UIButton correctly. You should use buttonWithType: method.
For example:
- (void)viewDidLoad {
[super viewDidLoad];
UIButton * btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn setFrame:CGRectMake(10.0, 10.0, 50.0, 50.0)];
[[self view] addSubview:btn];
}
You have placed square brackets at wrong places. Probably you are new to Objective-C. You should pay more attention to learning syntax before creating UI based apps.
UIBUtton *btn = [UIBUtton] alloc] initWithFrame:CGRect(10,10,50,50)];
Should be:
UIBUtton *btn = [[UIBUtton alloc] initWithFrame:CGRect(10,10,50,50)];
Also
[self.view setNeedsDsiplay];
is not needed.

Create a custom UIButton class with delete function

I have a grid of UIButtons. When I hit an 'edit' button, I want a delete button to appear over each of these buttons, which when pressed, deletes the button (and associated data). A bit like apple's home screen, when you hold down a button and it starts to wiggle with an X in the corner.
According to this post: Subclass UIButton to add a property I can use Associative References to add a property to each of my buttons. I've tried to add a UIButton as a property of my custom UIButton but I can't seem to get it to appear and have the feeling this isn't the right way to go. Here's my custom button main:
#import "UIButton+Property.h"
#import <objc/runtime.h>
#implementation UIButton(Property)
static char UIB_DELETEBUTTON_KEY;
#dynamic deleteButton;
- (void)setDeleteButton:(UIButton *)deleteButton {
deleteButton = [UIButton buttonWithType:UIButtonTypeInfoDark];
deleteButton.frame = CGRectMake(100, 100, 50, 50);
objc_setAssociatedObject(self, &UIB_DELETEBUTTON_KEY, deleteButton, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIButton *)deleteButton {
return (UIButton *)objc_getAssociatedObject(self, &UIB_DELETEBUTTON_KEY);
}
#end
And here's where I add the buttons programmatically:
//Create a custom button for each custom book doc
for (int i = 0; i < [customBookDocs count]; ++i) {
BookDoc *customBookDoc = [customBookDocs objectAtIndex:i];
NSString *bookTitle = customBookDoc.book.title;
//create a button for each book
CGRect frame = CGRectMake(xCoord, yCoord, 200, 200);
UIButton *bookButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
bookButton.bookDoc = customBookDoc;
[bookButton setFrame:frame];
[bookButton setTitle:bookTitle forState:UIControlStateNormal];
[bookButton addTarget:self action:#selector(bookButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
xCoord += 250;
[self.view addSubview:bookButton];
[self.view addSubview:bookButton.deleteButton];
}
Is there an easier more sensible way to do this? Or am I on the right track?
ORIGINAL RESPONSE BEGAN:
... Someone else may have more to say about that, but I'm not sure why you'd need to use object association here. You can certainly add another button to your button as a property using regular subclassing, which is the route that I would take. ...
EDITS BELOW:
I thought that I had subclassed a UI control directly, but I realized that I was mistaken when I went to look for the code. #Joe rightly pointed out in the comments that there are issues with directly subclassing UI controls.
I was able to implement something like the functionality you described without using Associated Objects, by creating a wrapper class to hold the button and its related delete button. It works, but it's not very flexible, so I would generally recommend #Joe's method as a better solution.
Here's the relevant code:
I threw all of the code into the appDelegate to keep it simple. I don't recommend that in real life.
AppDelegate.m:
#implementation AppDelegate
#synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
UIButton *toggleDeleteButtons = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[toggleDeleteButtons setFrame:CGRectMake(20, 45, 280, 45)];
[toggleDeleteButtons setTitle:#"Toggle Delete" forState:UIControlStateNormal];
[toggleDeleteButtons addTarget:self action:#selector(toggleDeleteButtonAction) forControlEvents:UIControlEventTouchUpInside];
[[self window] addSubview:toggleDeleteButtons];
ButtonWrapper *myButtonWrapper = [[ButtonWrapper alloc] init];
[[myButtonWrapper button] setFrame:CGRectMake(20, 100, 200, 45)];
[[myButtonWrapper button] setTitle:#"This is my button" forState:UIControlStateNormal];
[[myButtonWrapper deleteButton] addTarget:self action:#selector(buttonDeleteRequested:) forControlEvents:UIControlEventTouchUpInside];
[[myButtonWrapper deleteButton] setTag:0];
[[self window] addSubview:[myButtonWrapper button]];
buttonWrapper1 = myButtonWrapper;
// Added instance called anotherButtonWrapper with tag 1, as above
// Added instance called stillAnotherButtonWrapper with tag 2, as above
[self.window makeKeyAndVisible];
return YES;
}
- (void)toggleDeleteButtonAction {
static BOOL deleteButtonsShown;
[buttonWrapper1 showDeleteButton:!deleteButtonsShown];
[buttonWrapper2 showDeleteButton:!deleteButtonsShown];
[buttonWrapper3 showDeleteButton:!deleteButtonsShown];
deleteButtonsShown = !deleteButtonsShown;
}
- (void)buttonDeleteRequested:(UIButton *)deleteButton {
// delete the specified button here
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Delete" message:[NSString stringWithFormat:#"Delete was pressed on button %i",[deleteButton tag]]delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
ButtonWrapper.m:
#implementation ButtonWrapper
#synthesize button;
#synthesize deleteButton;
- (ButtonWrapper *)init {
ButtonWrapper *newWrapper = [ButtonWrapper alloc];
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[myButton setFrame:CGRectZero];
UIButton *myDeleteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[myDeleteButton setFrame:CGRectMake(0, 0, 100, 40)];
[myDeleteButton setTitle:#"Delete" forState:UIControlStateNormal];
[myDeleteButton setHidden:TRUE];
[myButton addSubview:myDeleteButton];
[newWrapper setButton:myButton];
[newWrapper setDeleteButton:myDeleteButton];
return newWrapper;
}
- (void)showDeleteButton:(BOOL)showButton {
if (showButton) {
[[self deleteButton] setHidden:FALSE];
[[self deleteButton] setEnabled:TRUE]; }
else {
[[self deleteButton] setHidden:TRUE];
[[self deleteButton] setEnabled:FALSE];
}
}
#end
This solution did not require me to implement all of the UI properties, but it did require extra work to hook up the embedded delegates, which is cumbersome. There may be a way to pass the delegates into the wrapper at initialization, but I couldn't make it work.

Placing UIPopover correctly next to dynamic buttons

I'm creating a dynamic number of buttons(eventually, these will be placed inside a horizontal scroll view), and I'm displaying a popover when a button is pressed, but I need to set the popover to appear just to the right of the selected button, but at the moment, the popover appears at the same place every time...
This is what I'm trying:
for (int i=0; i < 10; i++) {
childButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
childButton.frame = CGRectMake(100*i, 170, 100, 30);
[childButton setTitle:#"Test" forState:UIControlStateNormal];
[childButton addTarget:self action:#selector(presentPopoverMenu) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:childButton];
}
Ultimately I'm going to create two rows of images, but this is just for testing...
-(void)presentPopoverMenu/*:(id)sender*/
{
MenuPickerController *mp = [[MenuPickerController alloc] init];
popoverMenu = [[UIPopoverController alloc] initWithContentViewController:mp];
popoverMenu.popoverContentSize = CGSizeMake(290, 300);
[popoverMenu presentPopoverFromRect:[childButton bounds]
inView:childButton
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:NO];
}
I'm thinking that it's probably just taking the value from last button, but I'm not sure, how I would do this otherwise...
Of course it uses last button value, because your ivar childButton points on it. Take not childButton but sender. Uncomment it and use. Also selector will be #selector(presentPopoverMenu:).

Resources