iOS2013. 7. 10. 15:08

PHP로 혹시 apple push notification service를 구축하려고 하시나요?

아마도 여러분께 이 포스팅이 유용할지도 모르겠네요.



최근 아이폰 어플리케이션을 런칭하면서 php로

apple push notification service를 구축하는 경험을 했습니다.


APNS(Apple Push Notification Service)는 애플 푸시 노티피케이션 서비스입니다.




아이폰에서 흔히 경험할 수 있는

어플리케이션에서 마치 문자메시지처럼 새로운 소식이나 메시지를 알려주는 서비스입니다.



대략적인 작동방식에 대한 설명을 드리자면

애플의 푸시 서버로 특정 애플 기기에 대한 token과 메시지 내용을 전달하면

애플의 푸시서버는 이를 받아 해당 token에 해당하는 기기로

푸시 메시지를 쏩니다.




APNS에 대한 애플의 문서를 번역한 자료입니다. 구축전에 한번 가볍게 읽어보시면 좋을것 같습니다.




자 그럼 이제 PHP버전의 APNS 라이브러리를 소개합니다.



1. Esay APNS




2. APNS-PHP




3. PHP-APNS





PHP의 APNS 라이브러리는 위의 3가지로 크게 나뉩니다.

그중에서도 제가 가장 강추하는 라이브러리는 1번에 소개된 EASY APNS 입니다.


추천하는 이유는 구축하기 가장 쉽고, 메뉴얼도 가장 깔끔합니다.


EASY APNS의 설치는 크게 4단계로 구분됩니다.


1. PHP 소스코드 업로드하기

2. mysql 테이블 생성하기

3. 노티피케이션 전송을 위한 cron job 설정하기

4. 아이폰 어플리케이션 소스코드에 APNS 적용하기



각각의 설치 방법은 웹사이트에 방문하시면 아주 친절하게 소개가 되어있습니다.

EASY APNS는 개발버전과 배포버전을 구분하여 push 보내기가 가능하며

사용자가 해당 어플리케이션을 삭제하였는지 feedback에 대한 처리도 하기 때문에

매우 유용합니다.


다만 이것을 구현하실 여러분들이 조금 더 수고해주셔야 하는부분은


EASY APNS의 매뉴얼대로 구축을 마치고 테스트를 하시면

EASY APNS는 사용자의 push 요청을 "큐"로 쌓아두고 있다가

cron job이 실행될때 이 쌓였던 push에 대한 요청들을 한꺼번에 처리합니다.


즉 cron job 설정을 한시간으로 해놓으면 push 가 매시각 한번씩 보내지는 것이고

1일단위로 설정해놓으셨다면 push가 일단위로 보내집니다.


그래서 혹시 즉시성을 요구하는 푸시 서비스를 구현하실때는

APNS 객체의 _pushMessage 메소드를 직접 호출하셔야 합니다.

이는 아마 소스코드 보시면 왠만한 PHP개발자 분들이라면 알수 있기에 자세한 설명은 넘어가겠습니다.



개발자분들의 칼퇴를 기원하며....


출처 : http://trend21c.tistory.com/1049

'iOS' 카테고리의 다른 글

PHP - cURL 비동기 통신  (0) 2013.12.01
셀 높이 조절  (0) 2013.06.25
앱 정보 가져오기 (앱이름, 버전)  (0) 2013.06.19
스토리보드 커스텀 셀 (공개)  (0) 2013.06.12
유니코드 출력  (0) 2013.02.01
Posted by 다오나무
iOS2013. 6. 19. 17:39

////////////////////////////////////////////////////////////////////////////////////

// 앱 정보 가져오기

NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];


// 앱 버전

NSString *szAppVersion = [ infoDict objectForKey:@"CFBundleShortVersionString"];

// 빌드 버전

NSString *szAppBuildVer = [ infoDict objectForKey:@"CFBundleVersion"];

// 앱 이름

NSString *szAppName= [ infoDict objectForKey:@"CFBundleDisplayName"];

Posted by 다오나무
iOS2013. 2. 1. 17:39

아이폰 어플을 개발하다 보면

그냥 키보드로 입력하는 문자이외에 유니코드에 들어있는 특수문자를 출력해야 할 필요가 생깁니다.

그럴 때 사용하는 필자의 방법인데요 별것 아니지만 혹시 생각이 안날까봐 적어둡니다.

1. 코드표를 확인한다.
2. unichar 변수에 코드값을 입력한다.
3. NSString로 변형한다.
4. 출력한다.


그럼 자세히 알아보죠..

1. 코드표를 확인한다.
여러가지 방법이 있겠지만 저같은 경우엔 맥북에서 확인할때 오픈오피스를 주로 사용합니다.


메뉴- 삽입 - 기호/문자표 를 클릭합니다.

필요한 특수문자를 잘 찾아봅니다. 폰트를 잘보시고요 아이폰에 있는 폰트를 선택해서 하셔야 하죠. 없는 폰트에서 고르면...
아이폰에서도 안나오겠죠?

2. unichar 변수에 코드값을 입력한다.

unichar unicode = 0x00A9;//©


3. NSString로 변형한다.

NSString * tmp = [NSString stringWithCharacters: &unicode length:1];


4. 출력한다.

NSLog(@"출력 %@",tmp);



쉽죠?
오늘은 여기까지..

Posted by 다오나무
iOS2012. 12. 18. 12:14

왼쪽 그림 하단의 버튼들은 상품카테고리를 나타낸다.  화면에 약 4개의 카테고리 밖에는 보여줄 수 없기 때문에 나머지 카테고리를 보여주기 위해 가로 스크롤뷰를 이용하여 좌우로 네비게시션이 가능하게 구성되어 있다.

아이폰 앱들 중 많은 것들이 상단이나 하단에 서브 메뉴의 형태로 가로 스크롤을 이용하는 것들이 많다. 예로 든 deal$의 경우에는 가로 스크롤에 추가로, 버튼을 클릭하였을 때 버튼의 크기를 키우는 효과를 추가로 사용하고 있다.

포스트에 첨부한 프로젝트 샘플은 간단히 가로 스크롤만을 구현한 것이다. 물론 개발은 비교적 쉽다. 기존의 스크롤뷰를   사용하면 된다.  

간단히 주석을 달아 놓았으니 HorizontalScrollMenuView와 HorizontalScrollMenuViewController 두 개의 클래스를 참고하면된다.













Posted by 다오나무
iOS2012. 10. 23. 13:44

원문(리스토어와 인엡 풀소스) - http://www.changwoo.net/bbs/bbsDetail.do?&num=545

애플의 정책이 바뀌어 non-consume in-app은 무조건 restore(복구) UI가 있어야 합니다, 안그러면 리젝사유가 되어 가슴아픈 경험을 하게 됩니다.

그에 따른 로직을 올립니다.

버튼에 이벤트로 
- (void) onClickRestore:(id)sender 를 호출하면
checkPurchasedItems -> 
 상황1 - 로그인 취소- > 
(void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error  
 상황2  - 로그인 성공 -> (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue

요런 로직입니당
로그인이 성공하면
paymentQueueRestoreCompletedTransactionsFinished함수에서 그동안 구입한 인엡에 대한 목록(product id)을 얻어옵니다
그러면 콜백받은 인엡목록과 본어플의 인엡의 코드(product id)를 검사해서 있는지 확인한후 처리 하시면 되겠습니다.




//복구 버튼 이벤트
- (
void) onClickRestore:(id)sender
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSArray *languages = [defaults objectForKey:@"AppleLanguages"];
    NSString *currentLanguage = [languages objectAtIndex:0];
     
    NSString *requestString = @"";
    //Language process
    if ([currentLanguage isEqualToString:@"ko"]) {
        //        requestString = @"복구 요청중";
    }else{
        //        requestString = @"Restore requesting";
    }
     
    HUD = [[MBProgressHUD alloc] initWithWindow:[[UIApplication sharedApplication] keyWindow]];
    //    HUD.center = CGPointMake(10, 110);
    [[[UIApplication sharedApplication] keyWindow] addSubview:HUD];
    HUD.delegate = self;
    HUD.labelText = requestString;
    [HUD show:YES];   
     
    [self checkPurchasedItems];
}
 
- (void) checkPurchasedItems
{
     
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
    [HUD show:NO];
     
}// Call This Function
 
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions
{
    NSLog(@"- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions ");
}
// Sent when an error is encountered while adding transactions from the user's purchase history back to the queue.
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
    NSLog(@"- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error ");   
    [HUD hide:YES];
}
// Then this is called
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
    NSLog(@"%@",queue );
    NSLog(@"Restored Transactions are once again in Queue for purchasing %@",[queue transactions]); 
     
    NSMutableArray *purchasedItemIDs = [[NSMutableArray alloc] init];
    NSLog(@"received restored transactions: %i", queue.transactions.count);
     
     
    //결재 기록이 없을때 alert 뛰우기
    if(queue.transactions.count==0){
        //        NSString *fileMessage = NSLocalizedString(@"NOTRESTORE", @"restore");
         
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        NSArray *languages = [defaults objectForKey:@"AppleLanguages"];
        NSString *currentLanguage = [languages objectAtIndex:0];
         
        NSString *failMessage;
        //Language process
        if ([currentLanguage isEqualToString:@"ko"]) {
            failMessage = @"구매 기록이 없습니다.";
        }else{
            failMessage = @"There is no record of your purchase.";
        }
         
        UIAlertView *resultView = [[UIAlertView alloc] initWithTitle:@"Failed"
                                                             message:failMessage
                                                            delegate:self
                                                   cancelButtonTitle:nil
                                                   otherButtonTitles:@"OK", nil];
        [resultView show];
    }
     
    for (SKPaymentTransaction *transaction in queue.transactions)
    {
        NSString *productID = transaction.payment.productIdentifier;
        [purchasedItemIDs addObject:productID];
        NSLog (@"product id is %@" , productID);
        // here put an if/then statement to write files based on previously purchased items
        // example if ([productID isequaltostring: @"youruniqueproductidentifier]){write files} else { nslog sorry}
         
        if([productID isEqualToString:KONGLISH_PRODUCTID])
        {
            //재구입확인dh
            NSLog(@"already buy");
        }
    
     
    [HUD hide:YES];
     
}







  • 2012/06/25 11:57답글

    신고

    유용하게 잘 사용했습니다~
    더 요약하자면 핵심은 이거죠

    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];//영구복원 실행

    - (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
    {
    if(error.code!=SKErrorPaymentCancelled)
    {
    //에러
    }
    else {
    //유저 취소시
    }
    }

    // Then this is called
    - (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
    {
    if(queue.transactions.count==0){
    //구매한 영구아이템이 없을경우
    return;
    }

    for (SKPaymentTransaction *transaction in queue.transactions)
    {
    //구매한 아이템들에 대한 복원 처리 


    }

  • 2012/07/02 22:15답글

    신고

    깔끔하군요 굿좝~~

Posted by 다오나무
iOS2012. 10. 22. 15:15

System sound를 사용할 경우...

App에서 system sound를 하나만 사용하면 상관 없으나 여러개를 자주 사용 할 경우에는 바로바로 resource를 해제 시켜줘야 memory 낭비를 막을 수 있다.

즉, app이 종료될 때 알아서 resource를 해제시키지만 app이 실행 되는 중에는 resource가 계속 생기므로 실시간으로 해제를 해줘야 한다는 것...(이해한 바로는)

따라서 call back 함수를 등록해 두고 재생이 끝나면 바로 해제를 시켜준다.
방법은  AudioServicesAddSystemSoundCompletion 를 통해 call back 을 등록하고
call back 내부에서 
AudioServicesRemoveSystemSoundCompletion 와 AudioServicesDisposeSystemSoundID를 호출해준다(순서를 바꾸면 안됨)
call back의 이름은 상관없으나 인자형식은 유지해야 하는 듯 하다.(SystemSoundIDvoid*)
자세한 내용은 AudioServicesAddSystemSoundCompletion의 reference를 참고.

/**

 *

 * System Sound call back method

 */

static void completionCallback(SystemSoundID soundID, void* mySelf)

{

    AudioServicesRemoveSystemSoundCompletion(soundID);   // 순서 주의!

    AudioServicesDisposeSystemSoundID(soundID);

}
 


NSString *path = [[NSBundle mainBundle]pathForResource:effectName ofType:file];

NSURL *url = [[NSURL allocinitFileURLWithPath:path];

SystemSoundID soundID;

AudioServicesCreateSystemSoundID((__bridge CFURLRef)url, &soundID);

AudioServicesAddSystemSoundCompletion(soundID, NULLNULLcompletionCallbackNULL);   // 종료 call back 등록

AudioServicesPlaySystemSound(soundID);

Posted by 다오나무
iOS2012. 10. 22. 13:37

 iOS 이전에는 모든 모달뷰를 제거하는데 parentView를 타고 올라가서 dismiss를 하는 방법을 사용했습니다.

하지만 iOS5로 버전이 올라가면서 parentView에 직접 dismiss를 하는것이 불가능해졌습니다.

그렇기 때문에 다른 방법을 통해서 parent의 ViewController로 접근하여 [self dismissModalViewController]를 호출해야합니다.

이를 해결하기 위해 NSNotification을 사용합니다.


 === parent View Controller ===

- (void)viewDidLoad

{

    [super viewDidLoad];

    [[NSNotificationCenter defaultCenteraddObserver:self selector:@selector(dismiss) 

name:@"BACKTOINDEXNOTE" object:nil];

}


-(void)dismiss

{

    [self dismissModalViewControllerAnimated:YES];

}


 위와같이 dismiss를 하고싶은 ViewController에서 NSNotificationCenter에 옵저버를 등록합니다. NotificationCenter에 신호가 post되면 dismiss라고 설정한 메서드 내의 코드를 수행하도록 했습니다.

 이제 자신이 원하는 순간에 NSNotificationCenter에 등록되어있는 "BACKTOINDEXNOTE" 에 대해 post를 해줍니다.


- (IBAction)homeButton:(id)sender{

    [self setModalTransitionStyle:UIModalTransitionStyleCoverVertical];

    

    NSNotification * notification = [NSNotification notificationWithName:@"BACKTOINDEXNOTE" object:nil];

    [[NSNotificationCenter defaultCenterpostNotification:notification];

}


 예시에는 홈버튼을 누르면 실행되는 메서드에서 노티를 보내도록 했습니다. 이와같은 방법을 통해 iOS5에서도 복수개의 모달뷰를 제거할 수 있습니다.

'iOS' 카테고리의 다른 글

IOS 기반 어플 효과음 재생  (0) 2012.10.22
AudioService resource 해제  (0) 2012.10.22
table view 2개 사용하기  (0) 2012.10.18
Mime Types  (1) 2012.10.05
AVAudioSession의 Category가 각각 무엇을 의미하는지 궁금합니다.  (0) 2012.10.05
Posted by 다오나무
iOS2012. 10. 18. 16:32

그냥 좀 간단한 list 같은 object 는 없나요?  tableview 가 생각보다 처음 쓰기에 복잡하네요.

지금 전 하나의 view 에 2가지 독립된 data 리스트를 보여주고 각각 입력하고 삭제하고 그런 일을 하려고 하는데요.
그래서 2개의 NSMutableArray를 만들어서 data 를 처리해서 첫번째 tableview 에는 잘 연결되서 제가 원하는 데로 작동합니다.
근데 두번째 tableview에는 연결하려고 하는데 잘 모르겠어요.

인터페이스로는 그냥 UIViewController 를 쓰고 있구요. UITableViewController 는 안씁니다.

어떻게 하는지 좀 구체적으로 설명해주면 저같이 얼마 안된 iphone 개발자에게 너무 도움이 많이 될꺼 같아요.

감사합니다.


자답: 
이런식으로 하니 되네요.

tableview 함수에 
if(tableviwe == myTable1){
  //처리
}else if(tableviwe == myTable1){
  //처리
}



Posted by 다오나무
iOS2012. 10. 5. 16:37

사운드


사용자는 아이폰에서 자신의 기호와 의도를 따르는 아주 멋진 사운드가 나길 바란다. 사용자는 가끔 설정에서 사운드가 꺼져있을때 조차도 소리가 나길 기대할 경우가 있는데, 이것은 사용자가 자신이 원하는 사운드는 듣고 싶어하고 자신이 원하지 않는 사운드는 듣고 싶어하지 않기 때문이다. 이러한 경향을 따르기 위해 아이폰 OS는 다음과 같은 기능을 지원하는 프로그래밍 인터페이스를 제공한다. 


- 어플리케이션의 사운드가 기기의 다른 사운드들과 어떻게 어울릴 것인지 정의한다

- 사용자의 기대대로 어플리케이션 사운드가 재생되고 있음을 보장한다


어플리케이션에서 사운드를 어떻게 다룰지 결정하기 전에 먼저 주요 설정 상황에서 사용자가 어떤 것을 기대하는지를 이해할 필요가 있다. 



1. 벨/무음(Ring/Silent) 스위치 - 사용자가 기대하는 것


사용자는 다음과 같은 경우에 벨/무음 스위치를 사용한다.


- 전화 벨소리나 문자 도착음 같이 예상하지 못했던 소리에 의해 방해받고 싶지 않을때

- 키보드나 기타 피드백 사운드와 같이 사용자 액션에 따른 소리를 듣고 싶지 않을때

- 사용자 액션의 주 목적이 아닌 게임 배경음이나 효과음 등을 듣고 싶지 않을때


예를 들어 극장에서는 다른 사람들을 방해하지 않기 위해 벨 소리를 끈다. 이 상황에서 사용자는 여전히 어플리케이션을 사용하고 싶어하지만 벨 소리같은 갑작스런 소리에 의해 놀라고 싶지는 않을 것이다. 


그러나 벨/무음 스위치는 사용자 액션이 직접적으로 소리에 관련된 것이거나, 사용자가 명시적으로 요청한 소리라면 이를 차단하지 않는다. 예를 들면,


- 미디어 재생 전용 어플리케이션에서 미디어를 재생하는 것은 사용자가 재생을 명시적으로 요청한 것이므로 차단하지 않는다.

- 시계의 알람은 사용자가 명시적으로 설정한 것이므로 차단하지 않는다.

- 언어 학습 어플리케이션에서의 음성을 재생하는 것은 사용자가 명백히 그것을 들으려 한 것이므로 차단하지 않는다.

- 음성 채팅 어플리케이션에서의 대화는 사용자가 음성 채팅이라는 단 하나의 목적을 가지고 어플리케이션을 시작하는 것이기 때문에 차단하지 않는다.


이러한 특성은 사용자 컨트롤 원칙을 따른다. 사용자가 명시적으로 요청한 소리를 들려주는 것이 적당한지 아닌지를 결정하는 것은, 기기가 아닌 사용자에게 달려 있기 때문이다.



2. 볼륨 조절 버튼 - 사용자가 기대하는 것


기기에서 나는 모든 소리는 볼륨 조절 버튼을 사용해 조절할 수 있다. 이는 벨/무음 스위치가 켜져있는지 꺼져있는지에 상관없이 어떤 소리든 언제라도 음량을 줄일 수 있다는 것을 의미한다.


어떤 경우에는 어플리케이션의 사용자 인터페이스를 통해 사용자가 볼륨을 조절할 수 있게 하는 것이 적절할 수도 있다. 예를 들어, YouTube에서는 슬라이더 컨트롤을 통해 현재 보고있는 비디오의 볼륨을 조절한다. YouTube가 실행 중인 동안에는 슬라이더와 볼륨 조절 버튼 둘 다 비디오 볼륨에 영향을 미친다. 이것은 어플리케이션이 실행되는 동안 슬라이더가 볼륨 조절 버튼의 프록시 역할을 하기 때문이다. 슬라이더는 어플리케이션의 볼륨과 전체 시스템 볼륨에 영향을 미치며 벨 소리에는 영향을 미치지 않는다.


이와 마찬가지로 볼륨 조절 버튼으로 어플리케이션이 현재 재생 중인 오디오를 조절하면 전체 시스템 볼륨도 같이 조절되며, 벨 소리에는 영향을 미치지 않는다. (오디오가 재생되지 않는 상태에서는 볼륨 조절 버튼으로 벨 소리 크기를 조절할 수 있다.)


이러한 특성 역시 사용자 컨트롤의 원칙을 따르는데, 이것은 기기에서 얼마나 큰 소리가 나야할지 결정하는 것은 언제나 사용자이기 때문이다.


가끔은 어플리케이션 내에서 상대적이거나 독립적인 볼륨 레벨을 사용할 수도 있지만, 이 경우에도 최종 오디오 출력은 볼륨 조절 버튼을 사용하든 어플리케이션 컨트롤을 사용하든 상관없이 항상 시스템 볼륨을 따라야 한다. 이것은 어플리케이션의 오디오 출력에 대한 제어 권한은 언제나 사용자에게 있음을 의미한다.



3. 헤드셋과 헤드폰 - 사용자가 기대하는 것


사용자가 헤드셋이나 헤드폰을 연결한다는 것은 현재 재생중인 오디오를 계속해서 듣되 개인적으로만 듣기를 원하는 것이다. 그렇기 때문에 사용자는 오디오가 중지되지 않고 계속해서 재생되길 원할 것이다.


반대로 사용자가 헤드셋이나 헤드폰을 빼는 경우에는 자신이 듣고 있던 것이 남에게 자연스레 알려지길 원하지 않을 것이다. 그렇기 때문에 어플리케이션은 현재 오디오 재생을 중지시키고 사용자로 하여금 명시적으로 재생을 다시 시작하게 해야 한다.



4. 어플리케이션의 오디오 특성 정의


어플리케이션에서 소리를 내거나 녹을을 하려면 그 어플리케이션의 오디오 특성이 기기의 오디오 환경과 어떻게 잘 어울릴 것인가를 결정해야 한다. 대부분의 어플리케이션에서 이는 아이폰 OS가 정의한 표준 인터랙션을 받아들이는 것을 의미하는데, 이러한 인터랙션은 시스템 사운드 서비스(System Sound Services)에 의해 지원되며 시작음이나 피드백, 경고음과 같은 부수적인 사운드만을 재생하는 어플리케이션에 잘 어울린다. 


그러나 어플리케이션의 주 기능으로 오디오를 재생, 녹음 하거나 긴 사운드트랙을 재생하는 상황에서는 표준 인터랙션이 적절하지 않다. 이 경우에는 어플리케이션의 오디오가 시스템 오디오 환경과 어울리는 방식에 영향을 줄 수 있는 오디오 특성 그룹을 정의할 수 있다. 오디오 세션 서비스(Audio Session Services)로 이러한 오디오 특성을 정의할 수 있다.


어떠한 특성을 정의하더라도 전화 기능은 현재 실행 중인 어플리케이션을 중단시킬 수 있다. 모든 어플리케이션은 사용자가 걸려온 전화를 받는 것을 막아서는 안된다.


1) 시스템 사운드 서비스(System Sound Services)


어플리케이션의 기능에 부차적인 짧은 소리를 재생할 때는 시스템 사운드 서비스를 사용한다. 구체적으로 다음과 같은 경우들이 이에 해당한다.


- 어플리케이션에서 경고음만을 재생하는 경우

- 30초 이내로 재생되는 경우

- 사운드의 레벨이나 위치 조정이 필요하지 않은 경우

- 언제나 기기에서 재생되는 다른 사운드들과 섞일 수 있는 경우

- 항상 벨/무음 스위치를 따라야 하는 경우


다음은 시스템 사운드 서비스에 정의되어 있는 함수들이다.


AudioServicesPlaySystemSound: 벨/무음 스위치를 따름. 다른 사운드와 섞일 수 있음. 예) 탭에 대한 피드백, 짧은 시작음

AudioServicesPlayAlertSound: 벨/무음 스위치를 따름. 다른 사운드와 섞일 수 있음. 예) 실패 경고음 혹은 새 메시지 알림음


NOTE: 기기 설정에서 진동이 켜져 있을 시, AudioServicesPlayAlertSound는 기기를 진동시킨다.


AudioServicesPlayAlertSound는 되도록 적게 사용하도록 하고, 사용자에게 상황을 알려주는 것이 적당한 경우에만 사용하도록 한다.


2) 오디오 세션 서비스(Audio Session Services)


어플리케이션의 오디오 특성에 대한 특정 그룹을 정의하는 경우 오디오 세션 서비스를 사용한다. 다음과 같은 경우가 이에 해당한다.


- 미디어 재생 어플리케이션이나 톤-매칭 게임과 같이 사운드가 어플리케이션의 주요 부분인 경우

- 30초 이상 재생되는 경우(어플리케이션에 필수적인 사운드가 아닌 경우도 포함)

- 사운드의 레벨이나 위치 조정이 필요한 경우

- 다른 어플리케이션의 사운드와 섞이지 않아야 하는 경우


간단하게 설명하면, 아이폰 OS는 오디오 세션을 사용하여 어플리케이션의 오디오가 기기에 어떻게 어울릴 것인지 결정하는 것을 도와준다. 오디오 세션을 사용하면 다음을 지정하는 것도 가능하다.


- 다른 소스로부터 얻은 오디오와 섞을 수 있는지 여부

- 벨/무음 스위치가 켜져 있는 경우에도 오디오를 재생할 수 있는지 여부


오디오 세션은 헤드셋 연결이 제거되는 것 같이 하드웨어적인 오디오 경로가 변화하는 경우나 강제 중단되는 경우에 이를 잘 처리할 수 있도록 노티피케이션을 제공한다.


어플리케이션의 오디오 특성 그룹을 정의하기 위해서는 오디오 세션 카테고리를 지정한다. 아이폰 OS는 7개의 카테고리를 제공하는데, 각각의 카테고리는 일정한 특성 그룹을 캡슐화한다. 카테고리를 적절히 지정하면 아이폰 OS가 그에 맞게 사운드를 제어해주어 사용자에게 더욱 좋은 경험을 제공할 수 있다.


대다수의 어플리케이션들은 하나의 카테고리만을 사용하며, 녹음과 재생같이 서로 매우 다른 방법으로 오디오를 사용하는 어플리케이션에 대해서만 하나 이상의 카테고리를 사용한다.


NOTE: 모든 어플리케이션에서 카테고리는 한번에 하나만 활성화 된다.


오디오 세션은 다음과 같은 오디오 프로그래밍 인터페이스에 의해 재생되는 사운드를 다룬다.


- Audio Queue Services

- OpenAL

- I/O 오디오 유닛

- AVAudioPlayer 클래스


짧거나 부가적인 사운드에 대해서는 시스템 사운드 서비스를 사용할 수도 있지만, 이 경우 오디오 세션이 어떤 식으로든 시스템 사운드 서비스가 재생하고 있는 사운드를 제어하고 있지는 않는지 확인해야 한다. 


다음은 오디오 세션 카테고리에 대한 설명이다.


UserInterfaceSoundEffects: 벨/무음 스위치를 따름. 다른 사운드와 섞일 수 있음. 예) 탭에 대한 피드백, 시작음

AmbientSound: 벨/무음 스위치를 따름. 다른 사운드와 섞일 수 있음. 예) 부가적인 소리 및 잡음

SoloAmbientSound: 벨/무음 스위치를 따름. 다른 사운드와 섞일 수 없음. 예) 게임 사운드트랙

MediaPlayback: 벨/무음 스위치를 따르지 않음. 다른 사운드와 섞일 수 없음. 예) 노래, 비디오, 스트리밍 오디오

LiveAudio: 벨/무음 스위치를 따르지 않음. 다른 사운드와 섞일 수 없음. 예) 음악이나 실시간으로 사용자가 만든 사운드

RecordAudio: 벨/무음 스위치를 따르지 않음. 다른 사운드와 섞일 수 없음. 예) 사용자 녹음

PlayAndRecord: 벨/무음 스위치를 따르지 않음. 다른 사운드와 섞일 수 없음. 예) 음성 변조 어플리케이션과 같이 동시에 오디오를 입출력하는 경우


NOTE: 오디오 세션 카테고리를 지정하지 않을 경우의 디폴트는 단독 배경음(solo ambient) 카테고리이다.



5. 복합적으로 사용하기


다음의 세가지 시나리오는 사용자에게 좋은 경험을 제공하기 위해 서로 다른 사운드 서비스를 어떻게 사용할 수 있는지를 보여준다.


[시나리오 1]


여러분이 블로깅 어플리케이션을 개발중이라고 가정해보자. 사용자는 텍스트와 이미지를 웹으로 올릴 수 있다. 짧은 시작음과 사용자 액션(포스팅이 완료되었을 때와 같은)에 따른 다양한 짧은 효과음들, 혹은 포스팅이 실패 했을때는 경고음을 들려주려 한다.


이러한 어플리케이션에서 사운드는 부가적이다. 주 작업은 오디오와는 아무런 관계가 없고 사용자는 소리 없이도 어플리케이션을 잘 이용할 수 있다. 이런 시나리오에서는 시스템 사운드 서비스를 사용해야 한다. 


[시나리오 2]


여러분은 새로운 언어를 학습할 수 있는 교육용 어플리케이션을 개발중이다. 어플리케이션이 시작되면 시작음을 들려주고, 사용자가 특정한 컨트롤을 누르면 피드백 사운드를 들려줄 것이다. 또한 사용자는 올바른 발음을 확인하기 위해 녹음된 단어나 문구를 들어볼 수 있다. 이렇게 서로 다른 사운드를 사용자가 기대하는 대로 들려주려면 다음을 따른다.


- 어플리케이션 시작음이나 피드백 사운드를 재생할때는 시스템 사운드 서비스를 사용한다.

- 미디어 재생(media playback) 카테고리를 적용한다. 녹음된 단어 등을 들려주는 것은 이 어플리케이션의 주 기능이기 때문이다.


이렇게 하면 벨/무음 스위치가 꺼져 있는 경우 시작음이나 피드백 사운드는 들리지 않는다. 그러나 사용자가 컨텐츠를 듣기 위해 명시적으로 선택하는 경우는 그것을 들을 수 있다.


[시나리오 3]


화면상의 캐릭터가 다양한 임무를 수행하는 게임의 경우를 생각해보자. 시작음이나 효과음, 사운드트랙 등을 들려줄 수 있다. 이 경우 사용자가 기대하는 사운드를 들려주기 위해서는 다음을 따른다.


- 어플리케이션 시작시에는 시스템 사운드 서비스를 사용한다.

- 단독음(ambient solo) 카테고리를 지정한다. 왜냐하면 사운드트랙이나 효과음은 중요한(그러나 필수적이지는 않은) 어플리케이션 경험의 일부이기 때문이다.


이렇게 하면 벨/무음 스위치가 꺼져 있는 경우 시작음이나 효과음, 사운드 트랙 등은 들리지 않는다. 그러나 음악(iPod)에서 노래를 재생하는 도중에 게임을 시작한다면 노래가 멈추고 사운드트랙과 효과음 등이 들린다.

Posted by 다오나무
iOS2012. 10. 5. 16:36

시작


아이폰 어플리케이션은 사용자가 지체되지 않고 사용할 수 있게 시작 즉시 실행되어야 한다. 시작시에는 다음 사항들을 따르도록 한다.


- 상태 바(status bar) 스타일을 적절하게 지정한다.

- 어플리케이션 로딩 이미지를 어플리케이션의 첫화면과 흡사하게 만든다. 이렇게 하면 어플리케이션 시작이 지연되는 느낌을 감소시킬 수 있다.

- 사용자가 어플리케이션을 즉시 사용할 수 있게 하지 못하는 About 창이나 스플래시 화면, 기타 시작 동작은 피하도록 한다.

- 기본적으로는 세로 방향(portrait)으로 시작한다. 만약 어플리케이션이 가로 방향(landscape)으로만 동작하게 하려면 양쪽 가로 방향을 모두 지원하도록 한다. 이때 사용자가 이미 기기를 가로 방향으로 들고 있었다면 그 방향 그대로 시작시키면 되고, 그렇지 않다면 기본적으로는 홈 버튼이 오른쪽으로 가게 띄운다.

- 어플리케이션이 마지막으로 실행된 시점의 상태를 복구한다.



종료


사용자가 다른 어플리케이션을 띄우거나, 전화를 받는 등의 기기 기능을 사용하면 어플리케이션은 종료된다. 여기서 중요한 것은 사용자가 어플리케이션을 종료시키기 위해 닫기 버튼을 누르거나 메뉴에서 종료를 선택하지는 않는다는 것이다. 종료시에는 다음 사항들을 따르도록 한다.


- 어느 시점에서든 어플리케이션을 종료시키는 노티피케이션을 받을 수 있도록 대비해야 한다. 그러므로 가능한 빨리, 그리고 합리적인 선에서 자주, 사용자 데이터를 저장한다.

- 종료시 현재 상태를 저장하되 가능한 가장 좋은 세부 수준의 상태를 저장한다. 예를 들어, 어플리케이션이 스크롤 가능한 데이터를 보여준다면, 현재의 스크롤 위치를 저장한다.


어플리케이션을 프로그램적으로 종료시키는 것은, 사용자에게는 그것이 충돌난 것처럼 보이기 때문에 절대 그렇게 해서는 안된다. 그러나 어떨때는 외부 환경으로 인해 어플리케이션의 기능을 실행시키지 못할 경우가 있는데, 이럴때는 사용자에게 현재 상태를 알리고 사용자가 무엇을 할 수 있는지 알리도록 한다. 이렇게 함으로써 사용자는 알맞는 조치를 취한 후 어플리케이션을 계속 사용할 것인지 아닌지를 선택할 수 있다.


외부 환경은 어플리케이션이 시작할때 뿐만 아니라 실행 중에도 바뀔수 있다. 어플리케이션의 주 기능을 사용할 수 없는 환경이 되었을때는 사용자에게 상황을 설명하고 무엇을 할 수 있는지 알려주는 화면을 보여주도록 한다. 예를 들어 iTunes 어플리케이션에서는 Wi-Fi가 연결되지 않았을때 왜 아이튠즈 뮤직 스토어에 접속할 수 없는지를 설명하고 어떻게 하면 이를 해결할 수 있는지 알려주는 화면을 보여준다.


<그림 4-1> 어플리케이션의 주 기능을 실행할 수 없을때



외부 환경 제약에 따른 또 다른 대안으로는 얼럿창을 표시하는 것을 들 수 있다. 예를 들어, 어플리케이션의 기능이 현재 위치를 사용하려 할 때 위치 서비스가 비활성화 되어 있다면, 얼럿창을 띄워 이것을 알려주고 사용자에게 위치서비스를 활성화 시킬 수 있는 기회를 제공하는 것이 효과적이다. 얼럿창은 디자인적인 면에서는 그리 유연성이 크지 않지만 다음과 같은 방법으로 사용하면 적당하다.


- 현재 상황을 매우 간단하게 설명한다.

- 현재 상황을 해결하기 위한 작업을 실행하는 버튼을 제공한다.

- 얼럿창을 매우 자주 띄우거나, 서로 다른 많은 상황에서 띄우는 것은 피한다.


모든 얼럿은 사용자들이 그것을 덜 보게 될수록 더 효율적이다.



설정


설정(Settings)은 아이폰에 내장된 설정 어플리케이션에 의해 접근할 수 있다. 사용자는 설정을 이용하기 위해 어플리케이션을 종료해야 하기 때문에, 한번 이상 지정하게되는 설정은 피하도록 하고, 가급적이면 설정이 불필요하게 어플리케이션을 단순화시키도록 한다.


옵션(Configuration options)은 어플리케이션 내에서 제공되며, 보통은 화면의 뒷면에서 보여준다. 설정과 다르게 옵션은 자주 바뀔 수도 있다.



방향


사용자는 언제든 기기의 방향(orientation)을 돌릴 수 있으며, 현재 보고 있는 화면이 그에 따라 적절히 반응하길 기대한다. 다음 사항들을 확인하도록 하자.


- 가속도계(accelerometer)의 값을 인식하고, 적당하면 모든 방향 변화에 대응해야 한다.

- 한쪽 방향으로만 보여야하는 사용자 인터페이스가 있는 경우는 그 방향으로 그대로 보여주고 기기 방향 변경에 반응하지 않는 것이 적절하다.


예를 들어 iPod에서는 현재의 기기 방향에 상관없이 언제나 동영상을 가로 방향으로 보여준다. 이것은 사용자가 동영상을 보기 위해 물리적으로 기기를 돌려야 하는 것을 의미하는데, 중요한 것은 이러한 과정에서 iPod은 "회전" 버튼 같은 것은 제공하지 않는다는 것이며, 대신 사용자는 동영상이 가로방향으로 뜨는 것을 알기 때문에 기기를 돌린다는 것이다. 


이처럼 어플리케이션이 특정 방향으로 보여져야 한다면 사용자가 기기를 물리적으로 돌리게 하고, 사용자에게 기기를 돌리라고 하는 컨트롤이나 제스처는 사용하지 않도록 한다.



선택


아이폰 OS는 사용자가 무엇인가 선택할 수 있게 하는 몇가지 요소들을 제공한다. 이러한 방법들은 사용자가 이미 익숙해져 있기 때문에 어플리케이션에서는 이것들을 사용하도록 한다. 일반적으로는 데스크탑 어플리케이션에서 사용하는 메뉴나, 라디오 버튼과 비슷한 모양의 컨트롤 등을 만드려고 하면 안된다. 아이폰 OS에서 제공하는 선택 요소들은 다음과 같다.


- 리스트(테이블 뷰): 목록의 행을 눌러 아이템을 선택한다. 대부분의 경우에 적당하다.

- 픽커(Picker): 원하는 값이 보일때까지 휠을 돌려서 선택한다. 

- 스위치: 둘 중의 한가지 값을 선택하기 위해 좌우로 컨트롤을 슬라이드한다. 스위치는 목록 내에서 간단한 선택을 제공하도록 의도되었다.



약관


약관(EULA, End-User License Agreement)은 어플리케이션을 사용하기 전에 사용자가 반드시 동의해야 할 책임 한계 등을 명시한다. 이러한 약관은 사용자가 어플리케이션을 구매하기 전에 미리 확인해 볼 수 있도록 앱스토어에서 보여지기 때문에, 어플리케이션에서는 모든 경우에 대해 약관을 표시하지 않도록 한다. 다만, 이는 설치와 설치후 첫 실행의 경우에도 해당하지만, 이 경우에는 완전히 제한하지는 않는다. 또한 어플리케이션 실행중에 약관을 표시하기 위한 사용자 인터페이스 요소를 제공해서도 안된다. 이러한 가이드라인을 따르면 사용자를 방해하지 않으면서도 필요한 동의를 얻을 수 있다.

Posted by 다오나무