About

This is one part of my current work for the iOS app. It is a flow form for new user to submit some basic personal informations. I did the basic UI/UX part, because we don’t have a designer yet. Some ideas are coming from Airbnb app. I really like it.

During the research of Airbnb iOS app, I found their app has problem with calculating the real height of keyboard on screen when user use a bluetooth keyboard. But it is not a big problem(They have fixed the problem by making the bottom bar transparent. It is really nice). This feature right now only does information gathering job. I will integrate more features, like finding friends, subscribing official accounts, into this part in the future.

The whole process has five pages. The user profile needs several required fields such as display name, birthday and real name. So the first two pages are for required information. If user didn’t fill them out, the white circle next button can not be pressed. After first two pages, the interface will tell user that he/she can skip current page.

i didn’t use navigation controller with five UIViewControllers, because I want to keep the bottom bar in the screen without transfer animation all the time. However, I did like the navigation transfer animation between pages, so I use CATransition to do the job:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Found firstView's index
NSUInteger i = 0;
while([self.view.subviews objectAtIndex:i] != firstView) {
++i;
}

// Remove firstView
[firstView removeFromSuperview];

// Inser secView at right index
[self.view insertSubview:secView atIndex:i];

// Create the transition
CATransition *animation = [CATransition animation];
// if you need delegate
animation.delegate = self;
[animation setType:kCATransitionPush];
if (fromLeft) {
[animation setSubtype:kCATransitionFromLeft];
} else {
[animation setSubtype:kCATransitionFromRight];
}
[animation setDuration:0.4];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];

// add animation to the self.view
[[self.view layer] addAnimation:animation forKey:@"transferViews"];

So, five pages (views) will be created at viewDidLoad, but only one of them will be added to the subview of the self.view at each time.

There are two things I cared about when keyboard came out. One is the bottom bar’s location that it should be at the top of the keyboard. And another is that textfield or textView should not be hidden by the keyboard.

So, I need to know when keyboard comes out and dismiss, and keyboard’s real height (not height got from notification, I will explain it) To do this, I need two notification: UIKeyboardWillShowNotification and UIKeyboardWillHideNotification.

1
2
3
4
5
6
7
8
9

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillOpen:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillClose:)
name:UIKeyboardWillHideNotification
object:nil];

And when keyboard comes out, I need to get the keyboard’s real height by:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)keyboardWillOpen:(NSNotification *)notification {
// get info from notification
NSDictionary *info = [notification userInfo];
// get keyboard's Frame
CGRect keyboardFrame = [info[UIKeyboardFrameEndUserInfoKey] CGRectValue];
// get keyboard's Frame in current superview
keyboardFrame = [self.view convertRect:keyboardFrame fromView:nil];
// get y position
CGFloat yPosition = keyboardFrame.origin.y;
// get real height show in the view
NSInteger height = [UIScreen mainScreen].bounds.size.height - yPosition;

...
}

The reason I got height by calculating is that when user use a physical keyboard, the keyboard on screen will show only part of itself. This will happen on iPad devices. I will show you the picture of Airbnb app. No offense.

The first image shows the normal software keyboard. the bottom bar moves up properly. But, in the second picture with physical keyboard, the bottom bar moves up like the first one. And more problem, in the third picture, the bar block one part of this page leading weird looking. The reason for this is using the height from userInfo directly.

1
2
NSDictionary *info = [notification userInfo];
CGFloat height = [info[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;

This height will not change when using a physical keyboard. The changed thing is the keyboard’s y position. Bingo!

So, after I got the real height, I can move the bottom bar up.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)keyboardWillOpen:(NSNotification *)notification {
...

// get delta y between bottom bar and keyboard
CGFloat deltaY = bottomBarView.frame.origin.y + bottomBarView.frame.size.height - yPosition;
// get keyboard showing animation time
double animationDuration;
animationDuration = [[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// move up the bottom bar
if (deltaY > 0) {
[UIView animateWithDuration:animationDuration delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
bottomBarView.center = CGPointMake(bottomBarView.center.x, bottomBarView.center.y - deltaY);
} completion:nil];
}

...
}

And for moving the textfield or textView, I chose to put them inside a scroll view. Then, by changing the contentInsets and contentOffset, I can move up the them. Something like this:

1
2
3
4
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, height, 0.0);
self.page_1.contentInset = contentInsets;
self.page_1.scrollIndicatorInsets = contentInsets;
// note: page_1 is an UIScrollView

I used RSKImageCropper for my image cropping currently. Great library. Thanks a lot. I may change it later, because I want to add some image effects for users to modify their head image.

For the last done circle, I used CABasicAnimation to draw it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// create a circle layer
CAShapeLayer *circle = [CAShapeLayer layer];
circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius, 2.0*radius) cornerRadius:radius].CGPath;
circle.position = CGPointMake(CGRectGetMidX(finishView.frame)-radius, CGRectGetMidY(finishView.frame)-radius);
circle.fillColor = [UIColor clearColor].CGColor;
circle.strokeColor = [UIColor whiteColor].CGColor;
circle.lineWidth = 5;

[self.finishView.layer addSublayer:circle];

CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
drawAnimation.duration = 0.55;
drawAnimation.repeatCount = 1.0;
drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
drawAnimation.toValue = [NSNumber numberWithFloat:1.0f];
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
drawAnimation.delegate = self;
[circle addAnimation:drawAnimation forKey:@"drawCircleAnimation"];

I think that is it. Hope you find some problem to tell me. Happing learning.