Access UIButtons from the interface - uiview

Background: I am creating a music app that gets data from my piano keyboard through CoreMIDI. When a note is played, it relays this to the GUI by showing the onscreen keyboard key as being pressed. The onscreen keyboard is represented by UIButtons for now. Each "key" (UIButton) has an identifier that corresponds to the MIDI value from my physical piano keyboard. For instance, middle C's UIButton will have a value of 60, since that is the value returned in MIDI data. C# is 61, D is 62, etc.
Question: How can I access all of the UIButtons from my window programatically? I need to access them by their "identifier" field, unless there's a more elegant way to do so. I haven't been programming for OSX for very long (actually only a few days) and I've literally watched about 100 videos on YouTube so I'm still learning. I have read a few posts that have said each UIButton is a subview of the main view. I couldn't figure out how to access the buttons via code though. Whatever suggestions you have will be very helpful! Thanks in advance.

Assuming you have all the buttons declared as outlets in your controller and connected them with your view.
Beware: Lots of untested pseudo-code will follow ;-)
#property IBOutlet UIButton *firstPianoKeyButton;
#property IBOutlet UIButton *secondPianoKeyButton;
#property IBOutlet UIButton *thirdPianoKeyButton;
... and so on ...
What you can do now is, to put the buttons one after the other into an array.
Let's say you have a controller with 12 keys, starting with what would usually be the middle C.
NSArray *pianoKeys = [NSArray arrayWithObjects:firstButton, secondButton, thirdButton, ..., nil];
Not nice, and there are more elegant and efficient ways to do this, but that should give the picture.
More on arrays in general: NSArray.
Check out this video here for a "better" way: Create Multiple UIButtons Programmatically. It's for iOS though, but again, it should give the picture :-D
Although check out this one: How do I create a basic UIButton programmatically?
Okay. Now, your controller will receive for example the NoteOn-Event of the D#, that corresponds to the MIDI note number of 63. Assuming this is an integer, we can use that one to access the corresponding element in our pianoKeys-array.
So we need an offset to get the right index, the offset will be 60. Why? Our array starts with index 0, but our note numbers of the 12 key piano start with 60.
On a full 88-key piano, the first MIDI note number starts with 21, so that would be the index in that case.
Now, that we can calculate the array index with the MIDI note number of the pressed key and can therefore display the corresponding button in the view as pressed.
[pianoKeys objectAtIndex:(63-60)];
Hope this is helpful.

This is the answer for my second problem where the button could only be clicked in the middle:
Here was the code (in awakeFromNib):
WhitePianoKey *button = [[WhitePianoKey alloc] initWithFrame:NSMakeRect(20, 20, 40, 300)];
[[window contentView] addSubview: button];
[button setButtonType:NSMomentaryPushInButton]; //I removed this line
changed to
WhitePianoKey *button = [[WhitePianoKey alloc] initWithFrame:NSMakeRect(20, 20, 40, 300)];
[[window contentView] addSubview: button];
It seems that setting the button type overrides my subclass and sets the size of the button back to the default size of whatever the style of the button is that I changed it to.
Thanks a lot again man!

Related

Custom UITableViewCell Edit View

I've been looking over countless questions and answers here on SO, but could not find one that would provide the answer for my specific problem.
In a nutshell: I am trying to implement a custom edit view for my UITableViewCells. I would like to keep the basic "delete" and "rearrange/drag" actions (although on opposite ends than in the default implementation and with custom icons), and I would also like to add a "duplicate" action to them and one that navigates to a specific view (separate from the one when you click the cell in normal state). Something like this:
Ideally, I would like to trigger this edit mode on all cells when I toggle edit mode with a separate button, and on individual cells with a left swipe (without the drag option if it is not possible on an individual cell).
I have sub-classed UITableViewCell and was able to add a swipe recognizer that reveals a view ("cellOptionsContainer") with the 3 custom buttons on the right, all of which I was able to make work using tags and delegates, but that is most certainly not the right way to do things because I've added them to the cell's content view (not knowing any better):
// Add container to hold options buttons
cellOptionsContainer = [[UIView alloc] initWithFrame:CGRectMake(cellWidth,
0.0f,
(dataBase.sizeInteraction * 3),
dataBase.sizeInteraction)];
[self.contentView addSubview:cellOptionsContainer];
// Add edit button
UIButton *cellOptionsButton = [[UIButton alloc] initWithFrame:CGRectMake((0.0f),
0.0f,
dataBase.sizeInteraction,
dataBase.sizeInteraction)];
cellOptionsButton.tag = cellID;
[cellOptionsButton setImage:[UIImage imageNamed:#"iconEditOutlined"] forState:UIControlStateNormal];
[cellOptionsButton setShowsTouchWhenHighlighted:YES];
[cellOptionsButton setAdjustsImageWhenHighlighted:NO];
[cellOptionsButton addTarget:self action:#selector(presentEditView:) forControlEvents:UIControlEventTouchUpInside];
//[cellOptionsContainer addSubview:cellOptionsButton];
[cellOptionsContainer addSubview:cellOptionsButton];
Meanwhile, I was also able to make work the standard edit view in my UITableView delegate (rearranging and deleting both works fine), but I was unable to augment this standard edit view with the added functionality and visual customization illustrated above. I cannot even find a way to change the standard icons...
Any help would be much appreciated; although, I am looking for Objective-C answers as I do not know Swift! I am also doing everything programmatically; I do not use interface builder.

Is it possible to add one UIButton object to multiple UIImageViews?

I have a scrollView and inside this scrollView, there are various PFImageView objects.
I need a single UIButton object on every PFImageView (same place and same button) that will load a subView with the information of the image tapped.
Is this possible?
So, let me get this straight... You want to create only one button, place it on multiple imageViews and have it respond differently (depending on which imageView was tapped)?
In this case...
No, you can't have 1 button in N places.
But... you can create N button objects and have them all assigned to 1 single target method in which... you can have different functionality based on the button's tag.
PS: UICollectionView would be cleaner approach.
You can't have a single control in multiple locations, it's that simple.
I saw your comment about not wanting to use a UICollectionView because it's not the design you're looking for, but I wonder what kind of design you want to create with UIScrollView only because UICollectionView doesn't works.
UICollectionView is not restrained to line/grid display of cells, but can be customized in a huge amounts of ways. You should watch the WWDC 2012 video "Introducing Collection Views" : at the end of it, some UICollectionView possibilities are shown and some of them are quite impressive.
Also, https://www.cocoacontrols.com/ website is a good place to find custom controls and even inspiration for your own design.
This is a little digression from the original issue, but I really think that a collection of UIImageView within an UIScrollView can be created with a UICollectionView, especially if you want to add some controls within your "cells".
Yes. You should use specific control events when adding actions to the button.
E.g. in my application i record/stop/cancel sound as long as user hold a button (drag out to cancel).. like a walkie talkie phone...
[recordButton addTarget:self action:#selector(recordStart) forControlEvents:UIControlEventTouchDown];
[recordButton addTarget:self action:#selector(recordStop) forControlEvents:UIControlEventTouchUpInside];
[recordButton addTarget:self action:#selector(recordCancel) forControlEvents:UIControlEventTouchDragExit];

Single Tapping not being detected on dynamically created uibuttons

It was necessary to create a subclass of UIButton, lets call it subButton. These subButtons are instantiated one by one and dynamically added to an UIScrollView object.
Unfortunately the subButtons aren't reacting as expected. After scrolling away from the first dynamic generated subButtons and then returning back to them. They loose their reactivity to single tapping. I can not explain this behaviour. Can somebody explain why its behaving this way?
Many thanks.
Source code
self.subButton = [[SubButton alloc]initWithFrame:CGRectMake(69.5, y, 201, 114)];
[self.subButton setBackgroundColor:[UIColor blueColor]];
self.subButton.imageData = imageData;
self.subButton.videoPath = videoPath;
self.subButton.vidIndex = _indexVideo +1;
[self.subButton drawVidPreview];
[self.subButton setTag:(_indexVideo +1)];
[self.subButton addTarget:self action:#selector(handleSingleTap:) forControlEvents:UIControlEventTouchUpInside];
[self.videoScroll addSubview:self.subButton];
[_videoPreviews addObject:self.subButton];
The subButton method drawVidPreview adds some subviews to itself.
Do the buttons not respond at all, or just not to single-tap? That might help us help you.
If they don't respond at all, I would check your view hierarchy to make sure no other view (perhaps invisible) are sitting above your buttons. Or, perhaps are the buttons not being added to the parent view you think?
Please make sure you are not adding the same UIGestureRecognizer instance to all of the UIButtons, instead for each UIButton you need to add a separate instance, however all gesture instance can point to the same target.
At last, i could solve the problem. The reason why the first subButtons did not respond to single tapping with UIControlEventTouchDown is because the uiScrollview content offset was set to a negative y value. This first of all was not necessary and was done out of pure laziness. I corrected it by giving it a positive value.
So my conclusion is, if you do not set the content offset or content size of the scrollView correctly, some dynamically added buttons to the scrollView might not respond to tapping appropriately. This is even the case if all Buttons are placed completely on a uiScrollview object.
This I could confirm by playing around with the content size of the uiScrollview instance too. I changed size of the y value of uiScrollviews instance just by few points and then some of the last buttons did not respond to tapping anymore.
Just by increasing the content size of the y value again, made the buttons responsive again.

How to reference a UILabel that was added through code so I can update position when iPad is rotated?

I added a UILabel subview using code. When the user rotates the iPad, I want to be able to tell that UILabel to shift its position to accomodate the new screen orientation.
I think I need to put my code in the willAnimateRotationToInterfaceOrientation function.
I want to be able to get at my UILabel and I see sample code that looks like the following to retrieve a reference to a subview:
UILabel *myLabel = (UILabel *) [self.subviews objectAtIndex:0];
How do I know what index my UILabel is at? The sample code seems to know that their subview is the first one (index 0).
Is there a way for me to find out what my UILabel's index is?
Is there another way to keep a reference to this UILabel that I create in code? If I made myLabel a variable of the class, will I be able to reference
Generally, in your UIViewController, you'd have an instance variable that was a pointer to your UILabel. Then you can access it anywhere in your controller's code.
Either have it in an instance variable (I like this, because it allows you to give nice names to your views), or set its tag property so you can find it again via [self.view viewWithTag:myTag] (the tag is just a number you assign to the view for exactly the purpose of finding it later).
Yet another possibility is not to do this, but instead set an autoresizingMask so it (for example) sticks to the upper left corner.

Adding Objects to View

Im making a game of sorts, where balls are added to the screen. I need to be able to add custom balls (different speeds/images, ect) at time intervals (i.e. ball type 1 gets added every 15 seconds, whereas ball type 2 gets added every 30 seconds). I need these balls to be UIImageView (at least with the setup I have right now to check collisions).
Any ideas on how I can do this?
Also, How to make the player (a UIImageView) the start button. I have an IBAction that starts the game. I cannot connect the UIImageView and the IBAction though...
Only a partial answer first, but really you don't want to use UIImageView for user interaction. A UIButton can be set to the type 'custom' and then display an image (you can set the image in interface builder or programmatically):
btnImage = [UIImage imageNamed:#"image.png"];
[btnMyButton setImage:btnImage forState:UIControlStateNormal];
It is then much easier to pick up IBAction etc.
As regards creating the balls dynamically, creating a new UIImageView programmatically is easy using the initWithImage method, I suggest you read up on the UIImageView class reference at http://developer.apple.com/library/ios/ipad/#documentation/uikit/reference/UIImageView_Class/Reference/Reference.html.
UIImageView inherits from UIView so after creating each image, just add it as a sub view using something like:
[self.view addSubView:newImageView];
Regarding the timer element, NSTimer is the place to look for this. I don't have much experience using these but it should be straightforward. Reference can be found at:
http://developer.apple.com/library/mac/ipad/#documentation/Cocoa/Reference/Foundation/Classes/NSTimer_Class/Reference/NSTimer.html
Hth

Resources