Iphone – Hover Over State for UIButton on iPhone

cocoa-touchiphoneuibutton

I successfully created a custom keyboard that functions similarly to Apple's keyboard. There is one thing that remains different. On Apple's iPhone keyboard the user can swipe their finger across the keyboard and all of the keys they pass though will pop-up. On my keyboard this does not happen. Swiping your finger across will just result in the first button I touched popping-up and when I am far enough distance away it goes back down. I am currently using this code for the pop-up keys:

UIImage *bigLetter = [UIImage imageNamed:@"letter1.png"];
[keyOne setImage:bigLetter forState:UIControlStateHighlighted];

keyOne is a UIButton, and I am overlaying an image of the larger character when the user "highlights" or taps the key on the keyboard. Is there a similar state that just "hovers over" so that if the user highlights the Q key and slides their finger over to the W key the W key gets highlighted?

Best Solution

I implemented a Periodic Table keyboard for my app, Compounds.

Don't use UIButtons.

To display your keyboard, use either: UIViews or CALayers to display the individual keys

OR

A static PNG. It is a lot easier on memory and "zippier". (this is what I have done for my yet to come update)

You must track all touches using the parent view. (small aside added at the bottom to explain why this is so) Implement the touches methods like so:

- (void)touchesBegan: (NSSet *)touches 
           withEvent: (UIEvent *)event {
    NSLog(@"TouchDown");

    CGPoint currentLocation = [[touches anyObject] locationInView:self.view];
    [self magnifyKey:[self keyAtPoint:currentLocation]];
}

-(void)touchesMoved: (NSSet *)touches 
          withEvent: (UIEvent *)event {
    NSLog(@"TouchMoved");

    CGPoint currentLocation = [[touches anyObject] locationInView:self.view];
    [self magnifyKey:[self keyAtPoint:currentLocation]];    
}


-(void) touchesEnded: (NSSet *)touches 
           withEvent: (UIEvent *)event{

    NSLog(@"TouchUp");

    CGPoint currentLocation = [[touches anyObject] locationInView:self.view];

    int key = [self keyAtPoint:currentLocation];
    [self selectKey:aKey];
}

These methods get the key and appropriately "magnify" the selected key.

I run the point against an array of CGRects to determine the key press (this is faster compared to hit testing).

- (int)keyAtPoint:(CGPoint)aPoint{

    int aKey=1;
    for(NSString *aRect in keys){
        if(CGRectContainsPoint(CGRectFromString(aRect), aPoint)){
            break;
        }
        aKey++;
    }

    if(aKey==kKeyOutOfBounds)
        aKey=0;
    NSLog([NSString stringWithFormat:@"%i",aKey]); 
    return aKey;
}


- (void)magnifyKey:(int)aKey{

        if(aKey!=0) {

            if(magnifiedKey==nil){

                self.magnifiedKey = [[[MagnifiedKeyController alloc] initWithKey:aKey] autorelease];
                [self.view addSubview:magnifiedKey.view];       

            }else{

                [magnifiedKey setKey:aKey];
                if([magnifiedKey.view superview]==nil)
                    [self.view addSubview: magnifiedKey.view];      

            }

        }else{

            if(magnifiedKey!=nil)
                [magnifiedKey.view removeFromSuperview];
        }
    }


    - (void)selectKey:(int)aKey{

        if(magnifiedKey!=nil){

            if(aKey!=0){

                [magnifiedKey flash];
                [self addCharacterWithKey:aKey];
            }
            [magnifiedKey.view performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:0.5];
        }
    }

There are a few methods in there for you to implement, but it is pretty straight forward. you are basically creating a view that is a magnified key. Then moving it around as the user slides their finger.


Aside:

You can't track touches with subviews because once a touch is being tracked by a view, it continues to track the touch until touchesEnded: (or touchesCancelled:) is called. This means once the letter "Q" is tracking a touch, not other key will have access to that touch, ever. Even if you are hovering over the letter "W". This is a behavior you can use to your advantage elsewhere, but in this situation you must work around it by having a "parent view" whose job it is to track touches.


(updated to fix memory leak)