왼쪽의 그림은 서클(또는 휠) 메뉴이다. 적당한 이름이 생각나지 않아 그냥 서클 메뉴라 칭했다. 좌우 제스처(gesture)를 이용해 메뉴를 좌우로 이동 시킨다. 그리고 메뉴가 보이는 상테에서 상하 제스처를 이용해 서클 메뉴를 이동 시킨다.
샘플로 첨부한 프로젝트는 이러한 애니메이션을 구현한 예이다. 실제 사용하기 위해서는 메뉴에 실제 버튼(예에서는 단순히 배경이미지만을 보여 준다.)을 추가해 이때 처리할 로직을 추가해야 한다.
원형의 메뉴를 위한 애니메이션을 위해 CGAffineTransformMakeRotation를 사용해 좌표 시스템을 이동시키는 방법을 사용했다. 그런데 이때 상하 제스처를 쉽게 사용하기 위해 CircleMenu라는 백그라운드 역할을 뷰위에 원형의 메뉴 이미지를 올려 놓았다. 이렇게 하지 않으면 좌표 시스템의 이동에 따라 상하 제스처를 제대로 이용할 수 없다.
간단히 코드를 살표 보자. 자세한 내용은 다음에 첨부한 샘플 프로젝트를 참고하라.
우선 Xcode에서 View-based Application 템플릿을 이용해 프로젝트를 생성하자. 그리고 서클 메뉴의 백그라운드로 이용할 CircleMenu 클래스를 생성한다. 실제 사용하기 위해서는 이 곳에 버튼과 관련된 내용을 추가해야 한다. 샘플에서는 제스처 이벤트만을 처리하고 있다.
그리고 CircleMenuViewController를 다음과 같이 수정한다.
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import <AVFoundation/AVFoundation.h>
#import <Audiotoolbox/AudioToolbox.h>
@class CircleMenu;
@interface CircleMenuViewController : UIViewController {
UIButton *toggleButton; // 화살표 버튼.
float lastRadian;
BOOL isLeft; // 화살표 이미지 상태.
}
@property (nonatomic, retain) CircleMenu *circle;
@property (nonatomic, retain) UIImageView *imageView;
@property (nonatomic, retain) IBOutlet UIButton *toggleButton;
- (void)rotateCircleMenu:(UISwipeGestureRecognizer *)recognizer;
- (void)moveToLeftOrRight:(UISwipeGestureRecognizer *)recognizer;
- (void)playSound;
@end
#import "CircleMenuViewController.h"
#import "CircleMenu.h"
#define degreesToRadian(x) (M_PI * (x) / 180.0)
@implementation CircleMenuViewController
@synthesize circle;
@synthesize imageView;
@synthesize toggleButton;
- (void)dealloc
{
[circle release];
[imageView release];
[toggleButton release];
[super dealloc];
}
// 생략...
- (void)viewDidLoad
{
[super viewDidLoad];
isLeft = YES;
self.circle = [[CircleMenu alloc] initWithFrame:CGRectMake(0.0, 0.0, 225.0, 225.0)];
self.circle.center = CGPointMake(420, 240);
[self.view addSubview:circle];
UIImage *image = [UIImage imageNamed:@"circle.png"];
imageView = [[UIImageView alloc] initWithImage:image];
self.imageView.bounds = self.circle.frame;
[self.circle addSubview:imageView];
lastRadian = 0.0;
// UISwipeGestureRecognizer 인스턴스 생성.
UISwipeGestureRecognizer *recognizerUp = [[[UISwipeGestureRecognizer alloc] initWithTarget:selfaction:@selector(rotateCircleMenu:)] autorelease];
// 스와이프 제스처를 인식하기위한 탭 수.
recognizerUp.numberOfTouchesRequired = 1;
// 스와이프의 방향: 상.
recognizerUp.direction = UISwipeGestureRecognizerDirectionUp;
[self.circle addGestureRecognizer:recognizerUp];
UISwipeGestureRecognizer *recognizerDown = [[[UISwipeGestureRecognizer alloc] initWithTarget:selfaction:@selector(rotateCircleMenu:)] autorelease];
// 스와이프 제스처를 인식하기위한 탭 수.
recognizerDown.numberOfTouchesRequired = 1;
// 스와이프의 방향: 하.
recognizerDown.direction = UISwipeGestureRecognizerDirectionDown;
[self.circle addGestureRecognizer:recognizerDown];
UISwipeGestureRecognizer *recognizerLeft = [[[UISwipeGestureRecognizer alloc] initWithTarget:selfaction:@selector(moveToLeftOrRight:)] autorelease];
// 스와이프 제스처를 인식하기위한 탭 수.
recognizerLeft.numberOfTouchesRequired = 1;
// 스와이프의 방향: 좌.
recognizerLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:recognizerLeft];
// UISwipeGestureRecognizer 인스턴스 생성.
UISwipeGestureRecognizer *recognizerRight = [[[UISwipeGestureRecognizer alloc] initWithTarget:selfaction:@selector(moveToLeftOrRight:)] autorelease];
// 스와이프 제스처를 인식하기위한 탭 수.
recognizerRight.numberOfTouchesRequired = 1;
// 스와이프의 방향: 우.
recognizerRight.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:recognizerRight];
}
// 생략...#pragma mark - 커스텀 메서드
// 메뉴 회전.
- (void)rotateCircleMenu:(UISwipeGestureRecognizer *)recognizer
{
NSLog(@"Before frame: %@", NSStringFromCGRect(self.circle.frame));
if (recognizer.direction == UISwipeGestureRecognizerDirectionUp) {
lastRadian += 26;
}
if (recognizer.direction == UISwipeGestureRecognizerDirectionDown) {
lastRadian -= 26;
}
[UIView beginAnimations:@"Rotation" context:nil];
[UIView setAnimationDuration:0.7];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
self.imageView.transform = CGAffineTransformMakeRotation(degreesToRadian(lastRadian));
[self playSound];
[UIView commitAnimations];
NSLog(@"Last radian: %f", lastRadian);
NSLog(@"After frame: %@", NSStringFromCGRect(self.circle.frame));
}
// 메뉴 이동(좌/우).
- (void)moveToLeftOrRight:(UISwipeGestureRecognizer *)recognizer
{
UIImage *image;
float moveCenterX;
if (isLeft && recognizer.direction == UISwipeGestureRecognizerDirectionLeft) {
moveCenterX = -100.0;
image = [UIImage imageNamed:@"icon_right.png"];
isLeft = NO;
}
if (!isLeft && recognizer.direction == UISwipeGestureRecognizerDirectionRight) {
moveCenterX = 100.0;
image = [UIImage imageNamed:@"icon_left.png"];
isLeft = YES;
}
// iOS4+: Blocks 사용.
[UIView animateWithDuration:0.7
delay:0.1
options:UIViewAnimationCurveEaseOut
animations:^{
self.circle.center = CGPointMake(self.circle.center.x + moveCenterX, 240);
self.toggleButton.center = CGPointMake(self.toggleButton.center.x + moveCenterX,self.toggleButton.center.y);
[self.toggleButton setImage:imageforState:UIControlStateNormal];
}
completion:^(BOOL finished){
NSLog(@"Done!");
}];
}
// 사운드 효과.
- (void)playSound
{
NSString *path = [[NSBundle mainBundle] pathForResource:@"sound" ofType:@"mp3"];
NSURL *url = [NSURL fileURLWithPath:path];
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
//[player setNumberOfLoops:1];
//[player playAtTime:1.5];
[player play];
}
@end 버튼을 추가하는 방법은 여러 가지가 있겠으나, 좀더 효과적인 것이 무엇일까 고민 중이다. 좋의 의견 있는 분들은 댓글을 남겨 주십시오!
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
And also design view according to the statusbar's hidden status ie. If statusbar is hidden I design my View with no statusbar in it. :) – Wolvorin Aug 9 at 9:37