Showing posts with label wallpaper editor. Show all posts
Showing posts with label wallpaper editor. Show all posts

Sunday, July 11, 2010

Wall Paper Editor: Dynamic toolbar.

OK, the next step is to change things so that instead of having an ugly button in the middle of the screen, I have a toolbar that can be shown or not show when the user does a single tap on the screen. This is a pretty common UI paradigm.


Creating toolbar in UIBuilder


First I created a toolbar in UIBuilder.  I could have done it dynamically, but this way I can hook everything up in UI builder.  Note because of the way I'm doing this, I can have multiple toolbars in UIbuilder and switch between them.

I hooked the toolbar up to the select picture, and added a new 'add text' window.
For now it's pretty simple, but I will likely expand it, and create better icons later.  I add events to my view controller for the various actions.


Changing creation of my classes


I change the creation of my views and variables so that I create a single view when the add button is chosen.  In addition I moved the creation of my various collections to viewDidLoad:


viewDidLoad
...
self.currentViews=[[NSMutableArray alloc]init];
NSMutableSet *viewSet=[[NSMutableSet alloc]init];
viewMoveHandler.childViews=viewSet;



- (IBAction) addWindow:(id)sender
{
TextDisplayView *testView=[[TextDisplayView alloc] init];
testView.view.multipleTouchEnabled=true;
testView.view.frame=CGRectMake(0,0,40,40);
[self.view addSubview:testView.view];
[self.currentViews addObject:testView];
[viewMoveHandler.childViews addObject:testView.view];
}




Intercepting single tap and displaying the toolbar


Next I intercept the single tap and when it arrives check to see if I'm in a view.  If so act as previously, otherwise toggle my toolbar.   Note that my toolbar has to be set to be at the bottom of the view, otherwise it appears at the top (since it's origin is 0,0).
I modified singleTap to handle this:


- (void) singleTap:(UIView *)subViewTapped
{
NSLog(@"Received single tap");

// Go ahead and stop.
[self.currentPinchView stopEditing];
self.currentPinchView=nil;
        // existing code.
for (TextDisplayView *view in self.currentViews)
{
if (view.view==subViewTapped)
{
self.currentPinchView=view;
keyHelper.view=view.view;
[view selectForEditing];
break;
}
}
if (self.currentPinchView==nil)
{
if (windowControl.superview==nil)
{
CGRect rect=windowControl.frame;
rect.origin.x=0;
rect.origin.y=self.view.bounds.size.height-windowControl.frame.size.height;
windowControl.frame=rect;
[self.view addSubview:windowControl];
}else {
[windowControl removeFromSuperview];
}

}
}

Wall Paper Editor: editing the text.

I want to edit the text in place as much as possible, but it's quite possible, even likely, that the onscreen keyboard will interfere.  I want to then translate the view that I'm using so that the text field is fully visible, while also maintaining the background.   The approach I choose for this is to move the view so that the
bottom line of the text field is just over the keyboard (If, and only if, the keyboard obscures the text field).  This can result in the text field scrolling over the top if this is a large text field, later I might add an scaling factor, or just allow scrolling in the text field.

For now I modified my KeyboardMover class so that it has two modes:
1) the previous behavior where it changes the size of the view
2) The new behavior so that it will calculate the bounds of the keyboard, and translate the superview of the given view, if there is a conflict:


// The keyboard helper is an object that when given a view will
// resize that view when the keyboard appears or disappears.   
// The dealloc of this will unregister the notification.
@interface KeyboardHelper : NSObject {

@private
UIView *view;
CGRect originalSize;
bool useCenter;


}
@property (nonatomic,retain) UIView *view;
@property (nonatomic) CGRect originalSize;
@property (nonatomic) bool useCenter;
- (id) initWithView:(UIView*) viewToUse;
@end

The new keyboard appearing has two modes based on the boolean.  I will probably clean this up later on, but it's a working first draft.  It took a bit to get the view behavior correct.



- (void)keyboardAppearing:(NSNotification *)notification {
NSDictionary *keys=[notification  userInfo];
CGRect myFrame=[self.view frame];
originalSize=myFrame;

NSValue *value=[keys objectForKey:UIKeyboardBoundsUserInfoKey];
CGRect bounds;
[value getValue:&bounds];
if (!useCenter)
{
view.frame=CGRectMake(myFrame.origin.x,myFrame.origin.y,myFrame.size.width,
  myFrame.size.height-bounds.size.height);
}else {
CGRect myFrame=[self.view.superview frame];
originalSize=myFrame;


NSValue *centervalue=[keys objectForKey:UIKeyboardCenterEndUserInfoKey];
CGPoint keyCenter;
[centervalue getValue:&keyCenter];
NSLog(@"eky center x %f, y %f",keyCenter.x,keyCenter.y);
NSLog(@"Old boudns (%f,%f) -- (%f,%f)",bounds.origin.x,bounds.origin.y,bounds.size.width,bounds.size.height);
bounds.origin.y = view.window.frame.size.height-bounds.size.height;
NSLog(@"New boudns (%f,%f) -- (%f,%f)",bounds.origin.x,
  bounds.origin.y,bounds.size.width,bounds.size.height);
CGRect viewInWindowCoords= [view convertRect:view.bounds toView:view.window];
NSLog(@"View in windowcoords (%f,%f) -- (%f,%f)",viewInWindowCoords.origin.x,
  viewInWindowCoords.origin.y,viewInWindowCoords.size.width,viewInWindowCoords.size.height);

double bottomY=viewInWindowCoords.origin.y+viewInWindowCoords.size.height;
if (bounds.origin.y
{
[UIView beginAnimations:nil context:view.superview];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationDuration:0.15];

CGPoint newCenter=view.superview.center;
newCenter.y -= (bottomY-bounds.origin.y);
view.superview.center=newCenter;
[UIView commitAnimations];
}    
}
}

And on the keyboard disappearing, just reset the view:



if (!useCenter)
view.frame=originalSize;
else {
[UIView beginAnimations:nil context:view.superview];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationDuration:0.15];
view.superview.frame=originalSize;
[UIView commitAnimations];
}
I enclosed the action in a simple animation, it makes it look much more natural.

Wall Paper Editor: editing text

The next stage now that I can move views around is to enable editing text.  I modified the text view editor nib so that it now includes a UITextView as a member.  I disabled editing and user interaction, and eliminated the scrollbars.  I sized it to the full size of the view.

Otherwise when the user attempted to drag the view, it would start editing instead.



 I have no scrolling, and the view itself has no interaction.  This way it is just a display for text.  I can programmatically control when it can be edited.

View Mover -- register taps


First the view mover was modified so that when a single tap is received it will call the proper routine in the delegate.
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@"touches count %d, event touches for my view %d",[touches count],
  [[event touchesForView:parentView] count]);
// Handle single tap.
if ([delegate respondsToSelector:@selector(singleTap:)])
{
for (UITouch *touch in touches)
{
if (touch.tapCount==1)
{
NSLog(@"Sending singleTap");
[delegate singleTap:currentView];
}
}
}
etc..


Controller use taps to set subview


Next the controller was modified so that it would call new routines on the subview to enable input.  First it would tell any current view to stop editing, and then tell the a subview (if one was selected) to start editing.  Note that because single taps are registered anywhere on the UIImageView, this means that selecting something outside your current subview will cause they editing to stop.
- (void) singleTap:(UIView *)subViewTapped
{
NSLog(@"Received single tap");

// Go ahead and stop.
[self.currentPinchView stopEditing];
for (TextDisplayView *view in self.currentViews)
{
if (view.view==subViewTapped)
{
self.currentPinchView=view;
[view selectForEditing];
break;
}
}
}



Subview respond to taps and enable input



Two new routines exist to handle stopping and starting the taps.  Just so that I can visually see something happening, I also change the alpha and background color as the sequence occurs.
- (void) selectForEditing
{
self.view.alpha=0.4;
self.view.userInteractionEnabled=true;
self.textView.userInteractionEnabled=true;
[self.textView becomeFirstResponder];
}
- (void) stopEditing
{
self.view.alpha=0.3;
self.view.backgroundColor=[UIColor greenColor];
[self.textView resignFirstResponder];
self.view.userInteractionEnabled=false;
self.textView.userInteractionEnabled=false;
}



Known Bugs


This has one major problem.  A view near the bottom will be hidden by the keyboard.  I need to change how things are displayed so that this does not occur.