2

I have two buttons (Button 1 and button 2) and if I press button 1, note1 starts and if i press button 2, note 2 starts.

So what I try to do is: press button one (note1 starts) and SLIDE to button2 and than should note2 start. As in, you slide without lifting your finger over a piano keyboard and all notes from c to h sound.

I did this with UIImageViews and I did it also with UIButtons.

If i press the c1pianoview it sounds a "c". If I press the d1pianoview it sounds a "d".

But if I slide without lifting my finger from c1pianoview to d1pianoview it only sounds a "c". What did I wrong? Can I also do this with UIButtons?

Touch down works but sliding doesn't work.

Can somebody help me, please? Here my code:

-(void)touchesBeganNSSet *)touches withEventUIEvent *)event
{
    UITouch *touch = [[event allTouches] anyObject];
    CGPoint location = [touch locationInView:touch.view];

    if(CGRectContainsPoint(c1pianoview.frame, location))
    { 
      NSString *path = [[NSBundle mainBundle]
      pathForResource: @"c1piano" ofType:@"mp3"];
      AVAudioPlayer* theAudio = [[AVAudioPlayer alloc] 
          initWithContentsOfURL:[NSURL fileURLWithPathath] error:NULL];
      [theAudio play];
    }

    if(CGRectContainsPoint(d1pianoview.frame, location))
    { 
      NSString *path = [[NSBundle mainBundle]
      pathForResource: @"d1piano" ofType:@"mp3"];
      AVAudioPlayer* theAudio=[[AVAudioPlayer alloc] 
          initWithContentsOfURL: [NSURL fileURLWithPathath] error:NULL];
      [theAudio play];
    }
}

Update:

I have a further question. Now I have two UIImageVIews on each other. A black piano key over two white piano keys. If I press on the black key it also sound the note from the white key under it.

How do I prevent this?

Jonny
  • 15,955
  • 18
  • 111
  • 232
user268086
  • 41
  • 1
  • 5

4 Answers4

5

I think a simpler solution is to create a superview that intercepts all the touches and decides what to do. Then in IB, you make your view an instance of PianoView, and make all the keys subviews. Each key would have a tag that identifies which key it is, so PianoView knows what note to play. PianoView has a currentView property which keeps track of the last view in touchesMoved so it doesn't keep trying to play the same key. I have a keyboard with similar but different requirements, and this approach works well.

The sample code below intercepts touches in hitTest:withEvent:. Then, in the touches* methods, it uses UIView's default implementation of hitTest:withEvent: to determine which key is being played. You'll have to modify it a bit if you want to support playing keys simultaneously with multi-touch.

@implementation PianoView

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    // intercept touches
    if ([self pointInside:point withEvent:event]) {
        return self;        
    }
    return nil;
}

-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UIView* view = [super hitTest: [[touches anyObject] locationInView: self] withEvent: nil];
    // highlight subview under touch
    if (view != nil && view != self) {
        if ([view isKindOfClass:[UIButton class]]) {
            [(UIControl *)view setHighlighted:YES];
        }
        [self setCurrentView:view];
        [self playKey:view];
    }
}

-(void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    // un-highlight everything
    for (UIView *subview in [self subviews]) {
        if ([subview isKindOfClass:[UIButton class]]) {
            [(UIControl *)subview setHighlighted:NO];
        }
    }
    [self stopPlaying];
    [self setCurrentView:nil];
}

-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UIView* view = [super hitTest: [[touches anyObject] locationInView: self] withEvent: nil];
    if (view != [self currentView]) {
        UIView *oldKey = [self currentView];
        // un-highlight
        if ([oldKey isKindOfClass:[UIButton class]]) {
            [(UIControl *)oldKey setHighlighted:NO];
        }    
        if ([view isKindOfClass:[UIButton class]]) {
            [(UIControl *)view setHighlighted:YES];
        }    
        [self stopPlaying];
        [self playKey:view];
        [self setCurrentView:view];
    }
}

-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    UIView* view = [super hitTest: [[touches anyObject] locationInView: self] withEvent: nil];
    for (UIView *subview in [self subviews]) {
        if ([subview isKindOfClass:[UIButton class]]) {
            [(UIControl *)subview setHighlighted:NO];
        }
    }
    [self stopPlaying];
    [self setCurrentView:nil];
}

@end
Christopher Pickslay
  • 17,523
  • 6
  • 79
  • 92
  • Good answer. Looks right, albeit didn't test it yet. Seems like no other answer on Stackoverflow got it right except this one. – Jonny Dec 15 '11 at 02:52
  • Found a bug causing crash inside `touchesMoved`. The second `[(UIControl *)oldKey setHighlighted:YES];` should be `[(UIControl *)view setHighlighted:YES];`. Other than that, this indeed seems to work. – Jonny Dec 15 '11 at 04:56
1

[Merged further question into original post]

Kev
  • 118,037
  • 53
  • 300
  • 385
user268086
  • 41
  • 1
  • 5
0

Don't you need the:

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
willcodejavaforfood
  • 43,223
  • 17
  • 81
  • 111
0

You can probably achieve this easily by using the "Touch Down" and "Touch Drag Inside" events of the UIButton.

If you want to use the -touchesMoved: approach, you can use a variable to keep track of the last button that triggered a sound and only play a sound if the current button is not the one that last played a sound.

In more detail:

Declare a UIView *lastButton; in your header and then:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];

if(CGRectContainsPoint(c1pianoview.frame, location) && lastButton != c1pianoview) {
    //play c1
    lastButton = c1pianoview;
}
else if(CGRectContainsPoint(d1pianoview.frame, location) && lastButton != d1pianoview) {
    //play d1
    lastButton = d1pianoview;
}
}

- (void)touchedEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    lastButton = nil;
}

- (void)touchedCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [self touchesEnded:touches withEvent:event];
}
Dimitris
  • 13,480
  • 17
  • 74
  • 94
  • Ok it doesnt work with TouchDragInside... can you help me with touchesMoved.. wich cariable? thx – user268086 Feb 07 '10 at 13:22
  • Sorry but it still doesnt work. If i move inside the c1pianoview or the d1pianoview it the note sounds by every little movement. So if i slide from c1pianoview to d1pianoview, there sound about 10 c-notes and 10 d-notes. =) but i only want hear one c-note and one d-note! Do know why i doesnt work? thx for helping! greez – user268086 Feb 07 '10 at 19:48
  • YEEEEESS maaaan... thanks a lot.. it works.. i did it wrong.. yeah you rock.. great thank you! – user268086 Feb 07 '10 at 19:56
  • Ok sliding works. but there is an onther small problem. If i click on c1-button five times it shoud sound five times a "C" so i made it with the touchesBegan method. This works but know the other problem is that if i click on c1-button and then slide inside the note sounds two times. I dont know if you understand my problem but thanks if you could help! greez – user268086 Feb 07 '10 at 20:56
  • I added a few more methods to my answer. They reset the `lastButton` variable whenever the finger is lifted. Have a look. – Dimitris Feb 08 '10 at 00:15
  • Sorry i got still two notes if i click and drag. But you said its possible to do it with buttons? I think it's easier with buttons isn't it? can you give me start help. sorry but i'm a noob. xD thanx a lot! – user268086 Feb 08 '10 at 11:05