[iOS] 바이너리 파일(Binary File) 쓰고 읽기

iOS에서 바이너리 파일을 생성하고 정수, 실수 그리고 문자열 값을 순서대로 쓴 후에 다시 읽기 위한 코드에 대해 정리해 봅니다. Objective-C에서 제공하는 파일 관련 클래스를 사용을 시도했으나 Ansi-C에서 제공하는 파일처리 함수를 사용했습니다. 진정 ‘구관이 명관’입니다 !!

UI 상에 정수, 실수 그리고 문자열을 입력받는 텍스트필드 컨트롤을 추가하여 UI를 구성합니다. 그 화면은 다음과 같습니다.
사용자 삽입 이미지
여기서 Save 버튼을 클릭했을때 입력된 값들을 바이너리 파일에 저장하는 코드 예는 다음과 같습니다.
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask,
                                                         YES);
    NSString *documentDir = [paths objectAtIndex:0];
    NSString *file = [documentDir stringByAppendingPathComponent:@"mydata.bin"];
    const char *szFileName =  [file UTF8String];
    FILE *pFile = fopen(szFileName, "wb");
    if(pFile != NULL) {
        int intValue = [tiInteger.text intValue];
        fwrite((void *)&intValue, sizeof(intValue), 1, pFile);
        
        float floatValue = [tiFloat.text floatValue];
        fwrite((void *)&floatValue, sizeof(floatValue), 1, pFile);

        NSString *stringValue = tiString.text;
        const char *bytesStringValue = [stringValue UTF8String];
        int lenStringValue = strlen(bytesStringValue) + sizeof(int);
        fwrite((void *)&lenStringValue, sizeof(lenStringValue), 1, pFile);
        fwrite((void *)bytesStringValue, sizeof(char), lenStringValue, pFile);
        
        fclose(pFile);
    }

iOS는 기본적으로 파일을 쓸 수 있는 디렉토리를 제한해 놓습니다. 파일을 쓸 수 있는 디렉토리를 얻기 위해 1번 ~ 5번 코드가 사용되었습니다. 뭐… 나머지는 fopen과 fwrite와 같은 파일 열기 및 쓰기에 대한 일반 C 함수를 사용했습니다.

다음은 UI 중 Restore 버튼을 클릭했을때 앞서 Save 버튼에 의해 저장된 값들을 복원하는 코드입니다.
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask,
                                                         YES);
    NSString *documentDir = [paths objectAtIndex:0];
    NSString *file = [documentDir stringByAppendingPathComponent:@"mydata.bin"];
    const char *szFileName =  [file UTF8String];
    FILE *pFile = fopen(szFileName, "rb");
    if(pFile != NULL) {
        int intValue;
        fread((void *)&intValue, sizeof(intValue), 1, pFile);

        float floatValue;
        fread((void *)&floatValue, sizeof(floatValue), 1, pFile);
        
        int lenStringValue;
        fread((void *)&lenStringValue, sizeof(lenStringValue), 1, pFile);
        
        NSMutableData *data = [NSMutableData dataWithCapacity:lenStringValue];
        char *bytesStringValue = (char *)[data bytes];
        fread((void *)bytesStringValue, lenStringValue, 1, pFile);
        
        fclose(pFile);
        
        tiInteger.text = [NSString stringWithFormat:@"%d", intValue];
        tiFloat.text = [NSString stringWithFormat:@"%f", floatValue];
        tiString.text = [NSString stringWithUTF8String:bytesStringValue];
    }

저장했던 값들에 대해서 순서대로 읽어 오고 있습니다. 주의할 점은 문자열 값을 읽기 위해서 char 배열을 생성하기 위해 18번 ~ 19번 코드처럼 NSMutableData 클래스를 사용했습니다. Objective-C는 new 연산자를 지원하지 않습니다 !!

[iOS] 이미지 회전하기

iOS에서 이미지를 화면에 표시하는 방법 중 2가지에 대해 정리해 봅니다. 첫번째는 UIView를 상속 받아 drawRect 함수를 재정의해 이미지를 직접 그리는 방식, 두번째는 이미지를 표시하는 UIImageView 컨트롤을 이용하는 방식입니다.

먼저 첫번째 방식인 UIView를 상속받아 drawRect 함수를 재정의해 이미지를 그리는 방식입니다. 여타 다른 코드는 소스 코드에 대한 첨부 파일을 살펴보면 될 것이고… 중요한 drawRect 함수만을 살펴보면 다음과 같습니다.
- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
        
    UIImage *img = [UIImage imageNamed:@"goon.png"];
    CGSize imgSize = [img size];

    static CGFloat angle = 0;
    angle += 5;
    CGFloat imgWidth = imgSize.width/1.5;
    CGFloat imgHeight = imgSize.height/1.5;
    CGFloat x = 80;
    CGFloat y = 120;
    
    CGRect rc = CGRectMake(x, y, imgWidth, imgHeight);
    
    CGContextTranslateCTM(context, imgWidth/2+x, imgHeight/2+y);
    CGContextRotateCTM(context, angle * M_PI/180);
    CGContextTranslateCTM(context, -imgWidth/2-x, -imgHeight/2-y);
    
    [img drawInRect:rc];
}

이미지 회전에 대해서 인지해야할 코드만을 설명드리면 8번의 angle 값이 회전값입니다. 그리고 12번과 13번 코드는 이미지를 표시할 화면상의 좌표입니다. 17번 ~ 19번 코드가 실제 이미지를 회전시키기 위한 코드로써 이미지를 중심으로 회전하기 위해 이동 변환과 회전 변환 다시 역이동 변환을 수행하고 있습니다. 이렇게 하면 다음처럼 화면에 이미지가 표시됩니다.

사용자 삽입 이미지
GooN이라는 버튼을 누르면 이미지가 5도씩 회전됩니다. 코드중 angle 변수를 static으로 지정한 이유가 바로 회전 각도 값을 유지하기 위함이였습니다.
이제 두번째로 UIImageView를 통해 이미지를 표시하고 회전해 보는 코드를 살펴보겠습니다. 일단 .xib 파일을 편집해 UI 단에 UIImageView 컨트롤을 원하는 위치에 배치하고 이 컴포넌트에 이미지 파일을 지정하면 쉽게 이미지를 표현할 수 있습니다. 이제 여기서 버튼을 클릭하면 이미지를 회전하는 코드를 살펴보면 다음과 같습니다.
-(IBAction)rotateAction:(id)sender
{
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:3];
    
    CGAffineTransform rotateTransform =
        CGAffineTransformMakeRotation(angle * M_PI / 180);
    
    angle = (angle == 180 ? 360 : 180);
    imageView.transform = rotateTransform;
    
    [UIView commitAnimations];
}

보시면 앞서 살펴본 첫번째 방법처럼 단순히 회전된 상태의 이미지를 화면에 찍는 것이 아닌 애니메이션 효과를 주어서 이미지를 회전시키고 있습니다. 이게 바로 UIImageView와 같은 컨트롤을 통해 이미지를 표현하는 가장 큰 장점입니다. 코드도 훨씬 간결하구요 !! 🙂 실행 결과는 첫번째에서 소개한 실행 화면과 동일하므로 생략하겠습니다. 차이점은 애니메이션을 통해 이미지가 회전된다는 것입니다.

끝으로 첫번째 방법과 두번째 방법에 대한 XCode 프로젝트에 대한 전체 소스 코드를 첨부합니다.

firstMethod.zip 파일이 UIView의 drawRect 함수의 재정의를 통해 이미지를 회전해 표시하는 것이고 secondMethod.zip 파일이 UIImageView 컨트롤을 이용해 이미지를 회전하는 것입니다.