보관물

Posts Tagged ‘아이폰’

iOS 개발 뉴스 : 8월 10일.

– Invoke에 관련 아티클 : http://blog.jayway.com/2011/08/08/invoke-any-method-on-any-thread/

– iOS개발 환경에서 SOAP 웹서비스를 사용하는 방법을 정리한 아티클 : https://beta-developer.omniture.com/en_US/blog/using-soap-web-services-on-the-iphone

카테고리:iPhone-iPad, Objective-C 태그:, , ,

iOS에서 애니메이션을 사용해 보자. (Core Animation)

최근에 Core Animation에 대해서 숙지하는 시간을 가졌습니다. 단순히 컨트롤이나 간단한 이미지에 대한 애니메이션 기능이라는 것은 알고 있었지만, 나중에 필요할 때에 찾아서 하면 되겠지라고 생각하고 있었는데, 마침 시간이 좀 있어서 정리를 해 보았습니다.
내용을 보면서 느낀 점은 Core Animation의 기능을 만드는 것은 마치 프리젠테이션 툴(파워포인트, 키노트 등)에서 오브젝트 애니메이션을 만드는 것과 비슷하다는 생각을 했습니다. 오브젝트를 움직이는 것도 중요하지만 잘 만들어진 애니메이션은 지나치지 않으면서도 꼭 필요한 곳에 적절하게 사용되는 것이 중요할 거라 생각되네요.

그럼 지금부터 간단한 설명과 효과에 대한 소스코드를 공유해 보려 합니다.

Core Animation을 이용하기 위한 기본적인 클래스는 ‘CAAnimation’ 입니다.. 그리고, CAAnimation에서 파생된 클래스가 아래와 같습니다.

  • CABasicAnimation:기본적인 애니메이션.(CAPPropertyAnimation의 파생클래스)
  • CAKeyframeAnimation:키프레임 애니메이션.(CAPPropertyAnimation의 파생클래스)
  • CATransition:레이어 전체에 대한 전환 효과.
  • CAAnimationGroup:여러 애니메이션을 같이 실행.

Core Animation classes and protocol

1. CABasicAnimation
CABasicAnimation은 레이어(CALayer)에 대해서 기본 애니메이션과 싱글 키프레임 애니메이션을 제공합니다.
아래 몇가지 프로퍼티를 가지고 있습니다.

  • fromValue: 애니메이션의 시작값
  • byValue: 애니메이션의 중간값
  • toValue: 애니메이션의 끝값

그럼 예제 소스를 하나 만들어 보겠습니다. 본 문서에서 예제들은 모두 뷰컨트롤러의 적당한 위치(viewDidLoad 등…)에 아래 소스를 넣어주면 됩니다.


    /* 움직이기 위한 대상 레이어를 만듭니다. */
    CALayer *layer1 = [CALayer layer];
    layer1.bounds = CGRectMake(0, 0, 40, 40);
    layer1.position = CGPointMake(0, 160);
    layer1.cornerRadius = 20;
    layer1.backgroundColor = [UIColor blackColor].CGColor;
    [self.view.layer addSublayer:layer1]; // 만든 레이어를 뷰 레이어에 올립니다.
    
    /* 이동할 위치 지정 */
    CGPoint pStart = CGPointMake(0, 160);
    CGPoint pEnd = CGPointMake(160,0);
    
    /* 애니메이션 작성 */
    CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"position"];
    ani.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    ani.fromValue = [NSValue valueWithCGPoint:pStart];
    ani.toValue = [NSValue valueWithCGPoint:pEnd];
    
    ani.repeatCount = HUGE_VAL;
    ani.duration = 10.0;
    [layer1 addAnimation:ani forKey:@"position"]; // 애니메이션을 원하는 레이어에 설정합니다.

위 소스를 통해 간단한 애니메이션을 구현할 수 있습니다. 검정색 원이 왼쪽 아래에서 오른쪽 위로 올라오는 것을 볼 수 있습니다. 위 소스의 애니메이션 작성 부분에서 아직 설명하지 않은 것은 세가지가 있습니다. timingFunction, repeatCount 그리고 duration 입니다. repeatCount와 duration은 이름과 같이 몇번 반복할 것인지와 애니메이션이 일어나는 시간을 설정하는 것 입니다.
timingFunction은 애니메이션이 일어나는 타이밍을 설정합니다. 타이밍 설정은 아래 네가지를 이용하여 설정할 수 있습니다.

  • kCAMediaTimingFunctionLinear : 일정한 속도로 재생
  • kCAMediaTimingFunctionEaseIn : 처음에 천천히 시작
  • kCAMediaTimingFunctionEaseOut : 마지막에 천천히 종료
  • kCAMediaTimingFunctionEaseInEaseOut : 천천히 시작하고 마지막에 다시 천천히 종료

이제 간단하게 객체를 이동하는 수준의 애니메이션을 작성할 수 있습니다.
이동 이외에도 Animation KeyPath의 동작은 아래와 같은 것도 있습니다.

2. CAKeyframeAnimation
키프레임 애니메이션을 만들어줍니다. 풀어서 얘기하면 정형적이지 않은 여러곳을 이동하는 프레임을 CGPathRef를 이용하여 설정하고, 그 위치를 특정 레이어가 이동하는 것 입니다.
기본적으로 사용하는 프로퍼티는 path, rotationMode가 있습니다.

간단한 예제를 보겠습니다.


    /* 움직이기 위한 대상 레이어를 만듭니다. */
    CALayer *layer1 = [CALayer layer];
    layer1.bounds = CGRectMake(0, 0, 20, 20);
    layer1.position = CGPointMake(200, 200);
    layer1.cornerRadius = 10;
    layer1.backgroundColor = [UIColor redColor].CGColor;
    [self.view.layer addSublayer:layer1]; // 만든 레이어를 뷰 레이어에 올립니다.
    
    /* 이동할 위치 지정 */
    CGMutablePathRef path1 = CGPathCreateMutable();
    CGPathMoveToPoint(path1,NULL,layer1.position.x, layer1.position.y);
    CGPathAddLineToPoint(path1, NULL, layer1.position.x, layer1.position.y + 200);
    CGPathAddLineToPoint(path1, NULL, layer1.position.x + 150, layer1.position.y + 150);
    CGPathAddLineToPoint(path1, NULL, layer1.position.x, layer1.position.y + 0);
    
    /* 애니메이션 작성 */
    CAKeyframeAnimation *moveAni1 = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    moveAni1.path = path1;
    moveAni1.rotationMode = kCAAnimationRotateAuto;
    moveAni1.duration = 5.0;
    [layer1 addAnimation:moveAni1 forKey:@"position"]; // 애니메이션을 원하는 레이어에 설정합니다.

fromValue, toValue를 여러 포인트를 지정하는 path로 변경하고, 방향이 바뀌는 것에 대응하는 rotationMode를 제외하면 이전에 기본 애니메이션과 큰 차이는 없습니다.

3. CATransition
레이어가 전환되는 것을 처리하는 애니메이션 클래스입니다.
기본적으로 사용하는 프로퍼티는 아래와 같습니다.

  • type : 전환 애니메이션 종류.
  • subtype : 전화 애니메이션 방향.
  • duration : 애니메이션 시간.

간단한 예제를 통해 알아 보겠습니다.


    /* 전환 레이어 생성 */
    CALayer *layer4 = [CALayer layer];
    layer4.bounds = CGRectMake(0, 0, 320, 480);
    layer4.position = CGPointMake(0, 0);
    layer4.anchorPoint = CGPointMake(0, 0);
    layer4.backgroundColor = [UIColor blueColor].CGColor;
    [self.view.layer addSublayer:layer4];

    /* 전환 애니메이션 생성 */
    CATransition *transition = [CATransition animation];
    transition.type = kCATransitionReveal;
    transition.subtype = kCATransitionFromTop;
    transition.duration = 2.0;
    transition.delegate = self;
    [layer4 addAnimation:transition forKey:nil];

type에 설정되는 속성은 네가지입니다.

  • kCATransitionFade : 레이어를 바로 표시
  • kCATransitionMoveIn : subtype에 설정된 방향으로 진행하며 나타남. 이전에 있던 레이어 위로 나타남
  • kCATransitionPush : subtype에 설정된 방향으로 진행하며 나타남. 이전에 있던 레이어를 밀어냄
  • kCATransitionReveal : 희미한 상태에서 서서히 나타냄

subtype에 설정되는 속성은 네가지가 있습니다.

  • kCATransitionFromRight : 오른쪽 방향에서 들어옴.
  • kCATransitionFromLeft : 왼쪽 방향에서 들어옴.
  • kCATransitionFromTop : 아래에서 위로 올라옴. (왜? 테스트 결과 그렇게 됨.)
  • kCATransitionFromBottom : 위에서 아래로 내려옴.??

4. CAAnimationGroup
여러 애니메이션을 동시에 실행하는 경우 사용합니다. 예를 들면, 사진을 삭제하면 작아지면서 쓰레기통으로 이동하는 것에서 CABasicAnimation을 통해 작아지면서, CAKeyframeAnimation을 통해 이동하는 것으로 처리가 가능합니다. 마지막에 쓰레기통이 흔들리는 것은 Delegate설정을 한 후에 animationDidStop에서 처리해 주면 됩니다.
역시, 예제를 통해 알아보겠습니다.


    /* 움직이기 위한 대상 레이어를 만듭니다. */
    CALayer *layer1 = [CALayer layer];
    layer1.bounds = CGRectMake(0, 0, 20, 20);
    layer1.position = CGPointMake(200, 200);
    layer1.cornerRadius = 10;
    layer1.backgroundColor = [UIColor redColor].CGColor;
    [self.view.layer addSublayer:layer1]; // 만든 레이어를 뷰 레이어에 올립니다.
    
    /* 이동할 위치 지정 */
    CGMutablePathRef path1 = CGPathCreateMutable();
    CGPathMoveToPoint(path1,NULL,layer1.position.x, layer1.position.y);
    CGPathAddLineToPoint(path1, NULL, layer1.position.x, layer1.position.y + 150);
    CGPathAddLineToPoint(path1, NULL, layer1.position.x + 100, layer1.position.y + 100);
    CGPathAddLineToPoint(path1, NULL, layer1.position.x, layer1.position.y + 0);
    
    /* 이동 애니메이션 작성 */
    CAKeyframeAnimation *moveAni1 = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    moveAni1.path = path1;
    moveAni1.rotationMode = kCAAnimationRotateAuto;
    moveAni1.duration = 5.0;
    
    /* 스케일 애니메이션 작성 */
    CABasicAnimation *scalAni1 = [CABasicAnimation animationWithKeyPath:@"transform"];
    scalAni1.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
    scalAni1.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)];
    scalAni1.duration = 5.0;
    
    /* 투명 애니메이션 작성 */
    CABasicAnimation *opacityAni1 = [CABasicAnimation animationWithKeyPath:@"opacity"];
    opacityAni1.fromValue = [NSNumber numberWithFloat:1.0];
    opacityAni1.toValue = [NSNumber numberWithFloat:0.1];
    opacityAni1.duration = 5.0;
    
    /* 애니메이션 그룹 작성 */
    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.animations = [NSArray arrayWithObjects:moveAni1, scalAni1, opacityAni1, nil];
    group.duration = 5.0;
    
    [layer1 addAnimation:group forKey:nil]; //레이어에 그룹애니메이션 적용

그룹 애니메이션은 animations속성에 NSArray타입의 여러 애니메이션을 적용해 주는 것 만으로 애니메이션 동작이 가능합니다.

이상 CAAnimation의 기본적인 동작에 대해서 알아보았습니다. 간단한 앱내의 컨트롤들의 효과는 가능해 보이며, 재미있는 앱을 만들어 내는데 양념 역할을 충분히 할 수 있을 거라 생각합니다.
하지만, 처음에도 언급했던 것 처럼 지나친 애니메이션 사용은 사용자에게 피로감을 줄 수 있으니 잘 기획되어서 사용 해야겠습니다.

긴글 읽어주셔서 감사합니다.

추)
본문을 보시고, 코드 실행이 안되신다는 분들이 있습니다.
아마도 프레임워크 추가와 헤더 import에 대한 언급이 없어서 그런거라 판단됩니다. 그래서 간단한 이미지를 몇가지 첨부하겠습니다.
1. 프로젝트에 QuartzCore.framework 추가

2. 소스코드 추가 부분에 ‘QuartzCore/QuartzCore.h’ 임포트

3. 구현 부분

iOS 개발에 사용되는 주요 컴포넌트 크기.

iOS개발을 진행하며 화면에 표시되는 주요 컴포넌트에 대한 크기를 조금 막연하게 알고 있는 경우가 있어 정리해 봅니다.
나중에 기회가 되면, 이미지로 설명하겠습니다.
아래 내용에서 레티나 디스플레이의 경우는 모두 두배를 해 주어야 합니다. 통신사가 보이는 상태표시줄은 20px 입니다.

* 아이폰
1. 스크린 사이즈
– 세로 : 320 * 480 px
– 가로 : 480 * 320 px
2. 네비게이션바
– 세로 : 높이 44 px
– 가로 : 높이 32 px
3. 네비게이션바 버튼
– 세로 : 높이 30 px
– 가로 : 높이 24 px
4. 네비게이션바 버튼 내부 이미지
– 약 20 * 20 px
5. 툴바 : 높이 44 px
6. 툴바 버튼 : 높이 30 px
7. 툴바 버튼 내부 이미지 : 20 * 20 px
8. 탭바 : 높이 49 px
9. 탭바 이미지 : 30 * 30 px

* 아이패드
1. 스크린 사이즈
– 세로 : 768 * 1024 px
– 가로 : 1024 * 768 px
2. 네비게이션바 / 툴바 : 높이 44 px
3. 네비게이션바 버튼 / 툴바 버튼 : 높이 30 px
4. 네비게이션바 버튼 이미지 / 툴바 버튼 이미지 : 20 * 20 px
5. 탭바 : 높이 49 px
6. 탭바 이미지 : 30 * 30 px
7. 리스트 뷰

참고, 아이패드에 개발시에 필요한 이미지 종류를 정리해 둔 자료가 있습니다. 참고하세요. 링크 : http://goo.gl/efCkT

키보드에 기능키를 추가하는 방법…

키보드 상단에 기능키를 넣은 예제를 많이 볼 수 있습니다.
오늘은 이런 기능키를 넣는 예제를 간단하게 만들어 보려고 합니다. 이 샘플은 초보자를 위한 것이니 여러가지 스킬을 쓰지 않고 최대한 단순하게 만들 예정입니다.

기본 개념은 UITextView나 UITextField 클래스의 inputAccessoryView속성을 이용하여 개발자가 원하는 키보드 형태를 만드는 것입니다. 물론, View에 subView를 추가하는 것은 제약이 없으니, 우리가 원하는 어떤 형태의 뷰도 넣을 수 있습니다.

간단한 예제를 위해 KeyboardTest라는 View-based Application을 만듭니다.
KeyboardTestViewController.h을 열어 아래와 같이 수정합니다.

@interface KeyboardTestViewController : UIViewController {
    NSString *inputText;
    IBOutlet UITextField *inputField;
    IBOutlet UIButton *drawButton;
    IBOutlet UILabel *drawField;
}
@property(copy, readwrite) NSString *inputText;

- (IBAction)callDrawButton:(id)sender;
@end

inputText는 입력된 문구를 저장하는 문자열 인스턴스입니다.
그외에 우리가 문자열을 입력받을 inputField, 입력한 문자열에 대한 반영을 지시할 drawButton 마지막으로 입력한 문자열을 출력해 줄 drawField를 생성합니다.
IBOutlet의 경우 아래 그림과 같이 xib 파일도 수정해 줍니다.

다음에는 각 컨트롤을 File’s owner와 연결합니다.
처음으로 drawField와 맨위의 UILabel을 연결하고, inputField와 아래의 UITextField를 연결하고, 마지막으로 drawButton의 TouchUpInside와 callDrawButton을 연결합니다.

다음에는 뷰 로드 후에 작업을 진행합니다.
KeyboardTestViewController.m 파일의 -(void)viewDidLoad 함수의 주석을 풀고 아래와 같이 코딩합니다.

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    /* 키보드에 확장될 부분을 위한 뷰를 생성합니다. */
    UIView *inputAccessaryView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 768, 50)];
    inputAccessaryView.backgroundColor = [UIColor darkGrayColor];
    
    /* 첫번째 기능을 위한 버튼을 생성하고 기능을 입력합니다. - 입력된 내용을 지우는 기능을 하는 버튼입니다. - */
    UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    cancelButton.frame = CGRectMake(20, 7, 100, 37);
    [cancelButton setTitle:@"지우기" forState:UIControlStateNormal];
    [cancelButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [cancelButton addTarget:self action:@selector(cancel:) forControlEvents:UIControlEventTouchUpInside];

    /* 두번째 기능을 위한 버튼을 생성하고 기능을 입력합니다. - 미리 지정된 문구를 입력하는 기능을 하는 버튼입니다. - */ 
    UIButton *presetButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    presetButton.frame = CGRectMake(137, 7, 100, 37);
    [presetButton setTitle:@"지정문구" forState:UIControlStateNormal];
    [presetButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [presetButton addTarget:self action:@selector(preset:) forControlEvents:UIControlEventTouchUpInside];

    /* 확장뷰에 커튼을 서브뷰로 등록합니다. - */
    [inputAccessaryView addSubview:presetButton];
    [inputAccessaryView addSubview:cancelButton];

    /* inputField에 확장뷰를 대입합니다. - */
    inputField.inputAccessoryView = inputAccessaryView;
}

우선 위와 같이 viewDidLoad를 수정하면 키보드에 확장된 뷰가 표시될 것입니다.
이제 기능을 붙여 봐야겠죠. 기능은 위 주석에도 표시한 것과 같이 간단하게 두가지입니다. 모든 내용을 지우는 것과 미리 지정된 내용으로 입력하는 것입니다.

기능을 위해 아래 함수를 구현합니다.

- (IBAction)callDrawButton:(id)sender {
    inputText = inputField.text;
    [self godrawField:sender];
}


- (void)godrawField:(id)sender {
    [drawField setText:inputText];
}

- (void)cancel:(id)sender {	
    inputText = @"";
    inputField.text = inputText;
    
    [self godrawField:sender];
}

- (void)preset:(id)sender {
    inputText = @"미리 정의된 문구를 넣도록 만들었습니다.";
    inputField.text = inputText;
    
    [self godrawField:sender];
}

dealloc을 통해 메모리 관리하는 것을 잊지 말고, 이제 실행을 해 보면 아래와 같이 실행이 됩니다.

%d 블로거가 이것을 좋아합니다: