아이폰
델리마운트 위키
목차 |
모바일 웹 - 레티나 이미지 지원
- Retina display for mobile web
- Targeting the iPhone 4 Retina Display with CSS3 Media Queries
- How to make your web content look stunning on the iPhone 4’s new Retina display
앱의 레티나 디스플레이 확인
UIImage의 imageNamed: 메소드는 레티나 디스플레이인 경우, 인자로 받는 파일명 뒤에 @2x를 붙인 파일을 먼저 찾아보도록 설계되어있습니다.
이를 이용하여 앱이 실행되는 기기가 레티나 디스플레이인지 확인할 수 있습니다. 우선 1x1 px 이미지 RetinaDisplayCheck.png 파일과 2x2 px 이미지 RetinaDisplayCheck@2x.png 파일을 준비합니다. 두 파일을 프로젝트 리소스에 추가해준 후 다음과 같은 소스코드를 통해서 알아낼 수 있습니다.
UIImage* image = [ UIImage imageNamed: @"RetinaDisplayCheck.png" ];
if ( YES == [ image respondsToSelector: @selector( scale ) ] && 2.0f == image.scale )
{
// this is a Retina-display
}
else
{
// Not a Retina-display
// ex) iPhone 3GS, iPad, iPad2 ...and so on
}
아이콘
- 아이폰 기본 : Icon.png (57x57)
- 아이폰 소형 : Icon-Small.png (29x29)
- 아이폰4 기본 : Icon@2x.png (114x114)
- 아이폰4 소형 : Icon-Small@2x.png (58x58)
- 아이패드 기본 : Icon-72.png (72x72)
- 아이패드 소형 : Icon-Small-50.png (50x50)
- 아이폰4 & 아이패드 아이콘
- iOS Human Interface Guideline: Custom Icon and Image Creation Guidelines
!!아래 내용 제대로 정리할 것 프로젝트 설정 Info의 Icon Files 에서 파일명에 확장자까지 모두 입력해주는 것이 좋음 (프로젝트 서머리에서 확인 가능해짐)
애드 혹 (Ad Hoc)
푸시
APNS JSON 포멧
{ "aps": { "sound": "default", "alert": "blah blah", "badge": 1 } }
JSON
아이폰에서 사용할 수 있는 JSON 라이브러리
- SBJson - BSD 라이센스
SBJson
널리 쓰이는 JSON 라이브러리로, Mac과 iOS 환경을 지원합니다. JSON to Objective-C / Objective-C to JSON 상호 변환을 지원합니다. JSON 문법이 엄격하게 지켜져야 합니다.
SBJsonParser
JSON 데이터를 Objective-C 데이터로 변환하는 클래스입니다. 다음과 같은 형태로 사용합니다.
SBJsonParser* parser = [ SBJsonParser new ]; NSMutableDictionary object = [ parser objectWithString: jsonString ]; [ parser release ];
변환시 사용하는 메소드는 다음과 같이 제공됩니다.
- objectWithData:
- objectWithString:
- objectWithString: error:
세 메소드의 역할은 동일하며, 결과적으로 objectWithData: 메소드를 호출하게 됩니다.
메소드의 반환값은 JSON 데이터에 따라서 NSMutableDictionary / NSMutableArray / nil 중의 하나가 됩니다. 문법이 엄격하게 지켜진 데이터만 해석이 가능하며, 오류가 발생하면 nil 이 반환됩니다. JSON의 루트 엘리먼트는 object 또는 array 가 가능합니다. 반환된 NSMutableDictionary / NSMutableArray 는 하위의 데이터까지 모두 해석되어 아래의 데이터 변환규칙에 따라 Objective-C 객체로 완전히 대체되어있게 됩니다.
데이터 변환 규칙
JSON 데이터를 Objective-C 데이터로 변환하는 규칙은 다음과 같습니다.
- null > NSNull
- string > NSString
- array > NSMutableArray
- object > NSMutableDictionary
- true > [ NSNumber numberWithBool: YES ]
- false > [ NSNumber numberWithBool: NO ]
- integer up to 19 digits > [ NSNumber numberWithLongLong: integer ]
- all other numbers > NSDecimalNumber
SBJsonWriter
JSON 데이터를 Objective-C 데이터로 변환하는 클래스입니다. 다음과 같은 형태로 사용합니다.
SBJsonWriter* writer = [ SBJsonWriter new ]; NSString* jsonString = [ writer stringWithObject: jsonObject ]; [ writer release ];
변환시 사용하는 메소드는 다음과 같이 제공됩니다.
- dataWithObject:
- stringWithObject:
- stringWithObject: error:
어느 메소드를 호출하든 결과적으로 dataWithObject: 메소드를 호출하게 됩니다. 메소드의 반환값은 dataWithObject: 의 경우 NSData를 반환하지만, stringWithObject:, stringWithObject: error: 의 경우 NSString을 반환합니다. 만약 주어진 객체가 JSON으로 반환할 수 없는 경우에는 nil을 반환합니다.
두개의 BOOL 속성값이 있습니다.
- humanReadable
- sortKeys
두 값 모두 개발중에 사용하기 위한 것으로, humanReadable 값은 문자열에 줄바꿈 문자와 공백을 통해 사람이 읽기 수월하도록 라인과 들여쓰기를 추가해서 반환해줍니다. sortKeys는 object의 키 값에 따라 정렬하여 보여주기 때문에 두 JSON 객체를 눈으로 비교하거나 하는 경우에 사용합니다.
변환하도록 입력하는 object는 아무 객체나 가능하다고 명시되어있지만, 실상 변환되는 것은 NSDictionary와 NSArray 객체로 구분됩니다. SBJsonParser가 반환하는 객체와 동일한 구조로 만들어진 객체라면 정상적으로 NSString으로 반환됩니다.
데이터 변환 규칙
- NSNull > null
- NSString > string
- NSArray > array
- NSDictionary > object
- [ NSNumber numberWithBool: YES ] > true
- [ NSNumber numberWithBool: NO ] > false
- NSNumber > number
Pull Down to Refresh
- EGOTableViewPullRefresh - 이게 앱들이 사용중인 것이 아닌가 생각됩니다.
- EGOTableViewPullRefresh - 위의 enormego의 fork인데, 뭔가 개선사항이 있었나 조사정도의 목적으로 링크 남겨둡니다. 오리지널이 레티나 대응도 되고 제대로 업데이트 되는 듯
UINavigationBar 배경 커스터마이즈
UINavigationBar에 배경이미지를 변경하는 것은 약간 복잡한 과정이 필요합니다. 배경이미지를 뷰에 삽입하는 것은 네비게이션 바의 subview 순서가 영향을 받게 되어 화면에 정상적으로 표기되지 않는 문제가 있으며, UINavigationController의 navigationBar 속성이 readonly 이기 때문에 상속을 사용할 수도 없습니다. 이에 Objective-C의 카테고리 기능을 이용하게 되는데, 단순하게 카테고리로 메소드를 변경하게 되면 원본 메소드를 덮어쓰게 되기에 다음과 같은 형태로 조금 더 안전하게 할 수 있습니다.
우선, 배경 커스터마이즈는 UINavigationBar 클래스의 drawLayer:inContext: 메소드를 오버라이드 해서 구현하도록 하겠습니다.
UINavigationBar 클래스의 카테고리를 구현하고, main.m 파일도 조금 수정하도록 할 것입니다.
- UINavigationBarOfCustomBackgroundImage.h
@interface UINavigationBar ( CustomBackgroundImage )
- (void) drawLayerOverride: (CALayer*) layer
inContext: (CGContextRef) ctx;
@end
- UINavigationBarOfCustomBackgroundImage.m
@implementation UINavigationBar ( CustomBackgroundImage )
- (void) drawLayerOverride: (CALayer*) layer
inContext: (CGContextRef) ctx
{
// 아래의 코드는 원본인 drawLayer:inContext: 메소드를 호출하게 됨에 주의할 것. 이 메소드 내부에서 drawLayer:inContext: 를 직접 호출하게 되면 무한 루프에 빠지게 됨.
// [ self drawLayerOverride: layer
// inContext: ctx ];
if( YES == [ self isMemberOfClass: [ UINavigationBar class ] ] )
{
// imageNamed: 메소드는 자체적으로 캐쉬를 사용하기 때문에 성능상 큰 지장이 없음.
UIImage* image = [ UIImage imageNamed: @"custom_navigation_bar.png" ];
CGContextClip( ctx );
CGContextTranslateCTM( ctx, 0, image.size.height );
CGContextScaleCTM( ctx, 1.0, -1.0 );
CGRect rect = CGRectMake( 0, 0, self.frame.size.width, self.frame.size.height );
CGContextDrawImage( ctx, rect, image.CGImage );
}
}
@end
- main.m
void _override_instance_method_by_category_method( Class targetClass, SEL instanceSelector, SEL categorySelector )
{
Method instanceMethod = class_getInstanceMethod( targetClass, instanceSelector );
Method categoryMethod = class_getInstanceMethod( targetClass, categorySelector );
if( YES == class_addMethod( targetClass, instanceSelector, method_getImplementation( categoryMethod ), method_getTypeEncoding( categoryMethod ) ) )
{
class_replaceMethod( targetClass, categorySelector, method_getImplementation( instanceMethod ), method_getTypeEncoding( instanceMethod ) );
}
else
{
method_exchangeImplementations( instanceMethod, categoryMethod );
}
}
int main( int argc, char* argv[] )
{
NSAutoreleasePool* pool = [ [ NSAutoreleasePool alloc ] init ];
Class navigationBarClass = [ UINavigationBar class ];
_override_instance_method_by_category_method( navigationBarClass, @selector( drawLayer:inContext: ), @selector( drawLayerOverride:inContext: ) );
int retVal = UIApplicationMain( argc, argv, nil, nil );
[ pool release ];
return retVal;
}
main에서 _override_instance_method_by_category_method( ... )를 호출하는 순간부터, UINavigationBar의 drawLayer:inContext: 메소드가 호출되면 원래의 메소드가 아닌 drawLayerOverride:inContext: 메소드가 호출되게 됩니다. 원래의 메소드는 drawLayerOverride:inContext: 메소드 내에서 drawLayerOverride:inContext: 메소드를 호출하면 접근할 수 있습니다. 만약 원래의 메소드를 호출해주면 tintColor에 따른 그라데이션 배경을 그리게 되며, 이 위에 부분적으로 수정을 하고자 한다면 원래 메소드 호출 후에 그리고 싶은 내용을 추가해주면 됩니다. 완전히 새로운 배경으로 채우고자 한다면 원래의 메소드는 호출하지 않고 이미지만 출력하면 되겠습니다. 위의 소스코드는 세로화면에 대해서만 구현되어있으며, frame 사이즈에 따라 가로 화면에 대한 구현을 추가해주면 가로 화면에 대해서도 정상적으로 동작하게 됩니다.
