Jun 06

UITextView has square edges, and looks ugly compared to a UITextField!

Screen shot 2010-06-06 at 20.22.29

Unfortunately, there isn’t an option to give the UITextView a nice border. There are various ways to do this, such as creating your own background image for the UITextView, but I have found a relatively easy way to do it – and one that anyone without photoshop skills can achieve.

I achieve it by placing a UITextField behind the UITextView. However, in InterfaceBuilder you can’t change the height of a UITextField, so you have to edit the .xib file in Text Edit to change its height.

Here are the steps you need to do:

  1. Add a UITextField to your View and put a UITextView on top of it.
  2. Decide on how high you want your UITextView and resize it.
  3. Give the UITextField a tag of 999 (or whatever you want) (hit CMD-1 to bring up the attributes inspector to do this).
  4. Save the .xib file and then right click the file > Open With > Other > TextEdit
  5. Search for your tag, 999, and in the same section of the xml you will see something like: {{25, 199}, {275, 31}} (see below)
  6. You need to change 31 to the same height as your UITextView and then save the .xib file in TextEdit
  7. When you return to IB it will tell you the document has been modified, click Revert
  8. You should now see a tall UITextField behind your UITextView. Make sure your UITextView has a transparent background!
  9. <object class="IBUITextField" id="154341485">
    	<reference key="NSNextResponder" ref="774585933"/>
    	<int key="NSvFlags">292</int>
    	<string key="NSFrame">{{25, 199}, {275, 31}}</string> THIS IS THE LINE THAT YOU NEED TO EDIT.... CHANGE 31 TO THE HEIGHT OF YOUR UITEXTVIEW
    	<reference key="NSSuperview" ref="774585933"/>
    	<reference key="IBUIBackgroundColor" ref="157267157"/>
    	<bool key="IBUIOpaque">NO</bool>
    	<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
    	<int key="IBUITag">999</int>THIS IS THE TAG YOU CHOSE TO HELP YOU FIND THIS SECTION IN THE XIB FILE
    

    Here is my project if you need help seeing how I did it.

    xcodeProject

    Thanks

    written by admin \\ tags: , , , ,

    Mar 25

    When you develop forms or any screens with input fields, occasionally the inputs will be obscured by the keyboard when it appears. This is bad usability for the user who now has to input data without being able to see what they have typed! One solution is to slide the whole view so that the field being edited is always visible.

    Screen shot 2010-03-25 at 20.21.31Screen shot 2010-03-25 at 20.21.41Screen shot 2010-03-25 at 20.32.47

    This solution I provide adds a few methods to UIView (yes i know, adding categories to cocoa classes is naughty) – which will determine how much to slide the view based on the inputs position on the whole screen, and then slide the view at the same speed as the keyboard slide entry. It then will slide back to where it was when you are finished editing.

    It is pretty simple to do this – here is how I calculate where to scroll the view:

    - (void) maintainVisibityOfControl:(UIControl *)control offset:(float)offset {
    	static const float deviceHeight = 480;
    	static const float keyboardHeight = 216;
    	static const float gap = 5; //gap between the top of keyboard and the control
    
    	//Find the controls absolute position in the 320*480 window - it could be nested in other views
    	CGPoint absolute = [control.superview convertPoint:control.frame.origin toView:nil];
    
    	//If it would be hidden behind the keyboard....
    	if (absolute.y > (keyboardHeight + gap)) {
    		//Shift the view
    		float shiftBy = (deviceHeight - absolute.y) - (deviceHeight - keyboardHeight - gap - offset);
    		[UIView beginAnimations:nil context:nil];
    		[UIView setAnimationDuration:0.3f]; //this is speed of keyboard
    		CGAffineTransform slideTransform = CGAffineTransformMakeTranslation(0.0, shiftBy);
    		self.transform = slideTransform;
    		[UIView commitAnimations];
    	}
    }
    

    ..and then I reset the view afterwards using:

    - (void) resetViewToIdentityTransform {
    	[UIView beginAnimations:nil context:nil];
    	[UIView setAnimationDuration:0.3f]; //this is speed of keyboard
    	CGAffineTransform slideTransform = CGAffineTransformIdentity;
    	self.transform = slideTransform;
    	[UIView commitAnimations];
    }
    

    You only need to make minimal changes to your own code, and call these methods from your UITextFieldDelegate methods (or other control delegates):

    - (void) textFieldDidBeginEditing:(UITextField *)textField {
    	[self.view maintainVisibityOfControl:textField offset:0.0f];
    }
    
    - (void)textFieldDidEndEditing:(UITextField *)textField {
    	if (textField == currentControl) {
    		//If the textfield is still the same one, we can reset the view animated
    		[self.view resetViewToIdentityTransform];
    	}else {
    		//However, if the currentControl has changed - that indicates the user has
    		//gone into another control - so don't reset view, otherwise animations jump around
    	}
    }
    

    Here is a copy of the XCode project:
    xcodeProject

    Thanks

    written by admin \\ tags: , , , ,

    Mar 03

    Here i will show you how to animate an object along a path on a UIView. I will create the path and draw it onto the UIView so that you can see it, and then use the same path for the animation.

    I’m doing all of this within a UIView that i have added to my screen…

    Animate along a path

    Firstly, we will draw a curved line on the screen….

    //This draws a quadratic bezier curved line right across the screen
    - ( void ) drawACurvedLine {
    	//Create a bitmap graphics context, you will later get a UIImage from this
    	UIGraphicsBeginImageContext(CGSizeMake(320,460));
    	CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    	//Set variables in the context for drawing
    	CGContextSetLineWidth(ctx, 1.5);
    	CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
    
    	//Set the start point of your drawing
    	CGContextMoveToPoint(ctx, 10, 10);
    	//The end point of the line is 310,450 .... i'm also setting a reference point of 10,450
    	//A quadratic bezier curve is drawn using these coordinates - experiment and see the results.
    	CGContextAddQuadCurveToPoint(ctx, 10, 450, 310, 450);
    	//Add another curve, the opposite of the above - finishing back where we started
    	CGContextAddQuadCurveToPoint(ctx, 310, 10, 10, 10);
    
    	//Draw the line
    	CGContextDrawPath(ctx, kCGPathStroke);
    
    	//Get a UIImage from the current bitmap context we created at the start and then end the image context
    	UIImage *curve = UIGraphicsGetImageFromCurrentImageContext();
    	UIGraphicsEndImageContext();
    
    	//With the image, we need a UIImageView
    	UIImageView *curveView = [[UIImageView alloc] initWithImage:curve];
    	//Set the frame of the view - which is used to position it when we add it to our current UIView
    	curveView.frame = CGRectMake(1, 1, 320, 460);
    	curveView.backgroundColor = [UIColor clearColor];
    	[self addSubview:curveView];
    }
    

    Now we will create a keyframe animation, and a path that is the same as the line we just drew. We will also draw a circle, and animate it along that path:

    - (void) animateCicleAlongPath {
    	//Prepare the animation - we use keyframe animation for animations of this complexity
    	CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    	//Set some variables on the animation
    	pathAnimation.calculationMode = kCAAnimationPaced;
    	//We want the animation to persist - not so important in this case - but kept for clarity
    	//If we animated something from left to right - and we wanted it to stay in the new position,
    	//then we would need these parameters
    	pathAnimation.fillMode = kCAFillModeForwards;
    	pathAnimation.removedOnCompletion = NO;
    	pathAnimation.duration = 5.0;
    	//Lets loop continuously for the demonstration
    	pathAnimation.repeatCount = 1000;
    
    	//Setup the path for the animation - this is very similar as the code the draw the line
    	//instead of drawing to the graphics context, instead we draw lines on a CGPathRef
    	CGPoint endPoint = CGPointMake(310, 450);
    	CGMutablePathRef curvedPath = CGPathCreateMutable();
    	CGPathMoveToPoint(curvedPath, NULL, 10, 10);
    	CGPathAddQuadCurveToPoint(curvedPath, NULL, 10, 450, 310, 450);
    	CGPathAddQuadCurveToPoint(curvedPath, NULL, 310, 10, 10, 10);
    
    	//Now we have the path, we tell the animation we want to use this path - then we release the path
    	pathAnimation.path = curvedPath;
    	CGPathRelease(curvedPath);
    
    	//We will now draw a circle at the start of the path which we will animate to follow the path
    	//We use the same technique as before to draw to a bitmap context and then eventually create
    	//a UIImageView which we add to our view
    	UIGraphicsBeginImageContext(CGSizeMake(20,20));
    	CGContextRef ctx = UIGraphicsGetCurrentContext();
    	//Set context variables
    	CGContextSetLineWidth(ctx, 1.5);
    	CGContextSetFillColorWithColor(ctx, [UIColor greenColor].CGColor);
    	CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor);
    	//Draw a circle - and paint it with a different outline (white) and fill color (green)
    	CGContextAddEllipseInRect(ctx, CGRectMake(1, 1, 18, 18));
    	CGContextDrawPath(ctx, kCGPathFillStroke);
    	UIImage *circle = UIGraphicsGetImageFromCurrentImageContext();
    	UIGraphicsEndImageContext();
    
    	UIImageView *circleView = [[UIImageView alloc] initWithImage:circle];
    	circleView.frame = CGRectMake(1, 1, 20, 20);
    	[self addSubview:circleView];
    
    	//Add the animation to the circleView - once you add the animation to the layer, the animation starts
    	[circleView.layer addAnimation:pathAnimation forKey:@"moveTheSquare"];
    }
    

    To get all this running, you can use this init method:

    - (id)initWithFrame:(CGRect)frame {
    	if (self = [super initWithFrame:frame]) {
    		[self drawACurvedLine];
    		[self animateCicleAlongPath];
        }
        return self;
    }
    

    and use something like this in your ViewController….

    - (void)viewDidLoad {
    	UIView *customView = [[Canvas alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
    	customView.backgroundColor = [UIColor blackColor];
    	[self.view addSubview:customView];
    	[customView release];
        [super viewDidLoad];
    }
    

    Also…don’t forget to add your Quartz import:

    #import <QuartzCore/QuartzCore.h>
    

    I’m sure there are lots of better ways of doing this, such as using CALayers and adding CGImage to the layers. But that’s something I haven’t tried yet. The example above should be enough to get you started with animation along a path.

    Here is a copy of the XCode project:
    xcodeProject

    written by admin \\ tags: , , , , ,