views:

1280

answers:

3

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?

A: 

Take a look at the UIControlEvents enumeration. This is all of the events you can catch for a particular view.

If i had to guess I would say you could probably replicate this functionality using some combination of TouchDown along with the TouchDrag series. Use TouchUp to catch the which key was touched.

Lounges
A: 

You can use the touchesMoved:withEvent: method of your container UIView and hit test each subview (aka, key) with something like:

for(key in keys){
  location = [touch locationInView:key];
  if([key pointInside:location withEvent:event]){
    // if not already animated or otherwise triggered, trigger
  }
}

The idea is to process the touch sequence using the parent view. This is an easy way to avoid sequence termination when the touch is moved outside the "hot zone" for the (UIControl *)subview.

Toby Joe Boudreaux
+2  A: 

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)

Corey Floyd
Nice. It'll probably simplify the code a bit if you instantiate the `MagnifiedKeyController` right off the bat instead of lazily. Right now, I think you have a memory leak if the `magnifiedKey` property is a retain property. Even if it's an assign property, it'll be unusual to release an assign property ivar during dealloc.
Daniel Dickison
It is a retain property and it gets released in dealloc, but you're right: I need an autorelease in there when I instantiate it. You could instantiate in the beginning and get rid of a few "ifs". it's just one object, so pre-loading isn't a bad idea.
Corey Floyd