반응형

반응형

// StyleSheet.h

#import <Foundation/Foundation.h>

typedef enum : int
{
    LabelTypeName = 0,
    LabelTypeBirthdayDate,
    LabelTypeDaysUntilBirthday,
    LabelTypeDaysUntilBirthdaySubText,
    LabelTypeLarge
}LabelType;

@interface StyleSheet : NSObject

+ (void)styleLabel:(UILabel *)label withtype:(LabelType)labelType;
+ (void)styleRoundCorneredView:(UIView *)view;

+ (void)initStyles;
+ (void)styleTextView:(UITextView *)textView;

@end


// StyleSheet.m

#import "StyleSheet.h"
#import <QuartzCore/QuartzCore.h>
#import "BlueButton.h"
#import "RedButton.h"

#define kFontLightOnDarkTextColor [UIColor colorWithRed:255.0/255 green:251.0/255 blue:218.0/255 alpha:1.0]
#define kFontDarkOnLightTextColor [UIColor colorWithRed:1.0/255 green:1.0/255 blue:1.0/255 alpha:1.0]

#define kFontNavigationTextColor [UIColor colorWithRed:106.f/255.f green:62.f/255.f blue:39.f/255.f alpha:1.f]
#define kFontNavigationDisabledTextColor [UIColor colorWithRed:106.f/255.f green:62.f/255.f blue:39.f/255.f alpha:0.6f]
#define kNavigationButtonBackgroundColor [UIColor colorWithRed:255.f/255.f green:245.f/255.f blue:225.f/255.f alpha:1.f]
#define kToolbarButtonBackgroundColor [UIColor colorWithRed:39.f/255.f green:17.f/255.f blue:5.f/255 alpha:1.f]
#define kLargeButtonTextColor [UIColor whiteColor]

#define kFontNavigation [UIFont fontWithName:@"HelveticaNeue-Bold" size:18.f]
#define kFontName [UIFont fontWithName:@"HelveticaNeue-Bold" size:15.f]
#define kFontBirthdayDate [UIFont fontWithName:@"HelveticaNeue" size:13.f]
#define kFontDaysUntilBirthday [UIFont fontWithName:@"HelveticaNeue-Bold" size:25.f]
#define kFontDaysUntilBirthdaySubText [UIFont fontWithName:@"HelveticaNeue" size:9.f]
#define kFontLarge [UIFont fontWithName:@"HelveticaNeue-Bold" size:17.f]
#define kFontButton [UIFont fontWithName:@"HelveticaNeue-Bold" size:30.f]
#define kFontNotes [UIFont fontWithName:@"HelveticaNeue" size:16.f]
#define kFontPicPhoto [UIFont fontWithName:@"HelveticaNeue-Bold" size:12.f]
#define kFontDropShadowColor [UIColor colorWithRed:1.0/255 green:1.0/255 blue:1.0/255 alpha:0.75]

@implementation StyleSheet

+(void)styleLabel:(UILabel *)label withtype:(LabelType)labelType
{
    switch (labelType)
    {
        case LabelTypeName:
            label.font = kFontName;
            label.layer.shadowColor = kFontDropShadowColor.CGColor;
            label.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
            label.layer.shadowRadius = 0.0f;
            label.layer.masksToBounds = NO;
            label.textColor = kFontLightOnDarkTextColor;
            break;
        case LabelTypeBirthdayDate:
            label.font = kFontBirthdayDate;
            label.textColor = kFontLightOnDarkTextColor;
            break;
        case LabelTypeDaysUntilBirthday:
            label.font = kFontDaysUntilBirthday;
            label.textColor = kFontDarkOnLightTextColor;
            break;
        case LabelTypeDaysUntilBirthdaySubText:
            label.font = kFontDaysUntilBirthdaySubText;
            label.textColor = kFontDarkOnLightTextColor;
            break;
        case LabelTypeLarge:
            label.textColor = kFontLightOnDarkTextColor;
            label.layer.shadowColor = kFontDropShadowColor.CGColor;
            label.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
            label.layer.shadowRadius = 0.0f;
            label.layer.masksToBounds = NO;
            break;
           
           
        default:
            label.textColor = kFontLightOnDarkTextColor;
            break;
    }
}

+ (void)styleRoundCorneredView:(UIView *)view
{
    view.layer.cornerRadius = 4.f;
    view.layer.masksToBounds = YES;
    view.clipsToBounds = YES;
}

+ (void)initStyles
{
    // 내비게이션 바.
    NSDictionary *titleTextAttributes = [NSDictionary dictionaryWithObjectsAndKeys:kFontNavigationTextColor, UITextAttributeTextColor,
                                         [UIColor whiteColor], UITextAttributeTextShadowColor,
                                         [NSValue valueWithUIOffset:UIOffsetMake(0, 2)], UITextAttributeTextShadowOffset,
                                         kFontNavigation, UITextAttributeFont, nil];
   
    [[UINavigationBar appearance] setTitleTextAttributes:titleTextAttributes];
   
    //  배경 이미지로 설정.
    [[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"navigation-bar-background.png"] forBarMetrics:UIBarMetricsDefault];
   
    NSDictionary *barButtonItemTextAttributes;
   
    // 내비게이션 버튼.
   
    // 내비게이션 버튼 배경의 색상.
    [[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTintColor:kNavigationButtonBackgroundColor];
   
    barButtonItemTextAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                   kFontNavigationTextColor, UITextAttributeTextColor,
                                   [UIColor whiteColor], UITextAttributeTextShadowColor,
                                   [NSValue valueWithUIOffset:UIOffsetMake(0, 1)], UITextAttributeTextShadowOffset, nil];
   
    [[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTitleTextAttributes:barButtonItemTextAttributes forState:UIControlStateNormal];
   
    NSDictionary *disabledBarButtonItemTextAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                                         kFontNavigationDisabledTextColor, UITextAttributeTextColor,
                                                         [UIColor whiteColor], UITextAttributeTextShadowColor,
                                                         [NSValue valueWithUIOffset:UIOffsetMake(0, 1)], UITextAttributeTextShadowOffset, nil];
   
    [[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTitleTextAttributes:disabledBarButtonItemTextAttributes forState:UIControlStateDisabled];
   
    // 툴바.
    // 툴바  배경 이미지.
    [[UIToolbar appearance] setBackgroundImage:[UIImage imageNamed:@"tool-bar-background.png"] forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
   
    // 툴바 버튼
    // 툴바 버튼에 어두운 배경 적용.
    // 툴바 버튼 배경색.
    [[UIBarButtonItem appearanceWhenContainedIn:[UIToolbar class], nil] setTintColor:kToolbarButtonBackgroundColor];
   
    // UIBarButtonItem에 흰색 텍스트 적용.
    barButtonItemTextAttributes = [NSDictionary dictionaryWithObjectsAndKeys:[UIColor whiteColor], UITextAttributeTextColor, nil];
   
    [[UIBarButtonItem appearanceWhenContainedIn:[UIToolbar class], nil] setTitleTextAttributes:barButtonItemTextAttributes forState:UIControlStateNormal];
   
    // 버튼.
    [[BRBlueButton appearance] setBackgroundImage:[UIImage imageNamed:@"button-blue.png"] forState:UIControlStateNormal];
    [[BRBlueButton appearance] setTitleColor:kLargeButtonTextColor forState:UIControlStateNormal];
   
    [[BRRedButton appearance] setBackgroundImage:[UIImage imageNamed:@"button-red.png"] forState:UIControlStateNormal];
    [[BRRedButton appearance] setTitleColor:kLargeButtonTextColor forState:UIControlStateNormal];
   
    [[BRRedButton appearance] setFont:kFontLarge];
   
    // 테이블 뷰.
    [[UITableView appearance] setBackgroundColor:[UIColor clearColor]];
    [[UITableViewCell appearance] setSelectionStyle:UITableViewCellSelectionStyleNone];
    [[UITableView appearance] setSeparatorStyle:UITableViewCellSeparatorStyleNone];
   
}

+ (void)styleTextView:(UITextView *)textView
{
    textView.backgroundColor = [UIColor clearColor];
    textView.font = kFontNotes;
    textView.textColor = kFontLightOnDarkTextColor;
    textView.layer.shadowColor = kFontDropShadowColor.CGColor;
    textView.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
    textView.layer.shadowRadius = 0.0f;
    textView.layer.masksToBounds = NO;
}

@end


반응형

'IPHONE' 카테고리의 다른 글

NSUserDefaults 사용하기.  (0) 2014.02.18
배열 정렬하기.  (0) 2014.02.17
prepareForSegue 사용하기.  (0) 2014.02.12
아이콘에 대한 설명  (0) 2014.02.08
iOS 6에서 오토 레이아웃 시작하기. 파트 2  (0) 2014.02.08
Posted by 컴스터
,
반응형

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{

// segue 속성에 설정한 identifier 로 분별한다.
    NSString *identifier = segue.identifier;
   
    if ([identifier isEqualToString:@"DetailViewController"])
    {
        // 먼저 데이터를 가져옴.
        NSIndexPath *selectedIndexPath = self.tableView.indexPathForSelectedRow;       
        NSMutableDictionary *dic_passedData = self.datas[selectedIndexPath.row];
       

        // 건너 받을 뷰컨트롤러를 얻어 온다.
        DetailViewController *detailViewController = segue.destinationViewController;

        // 데이터 넘기기.
        DetailViewController.data = dic_passedData;
    }

    else if([identifier isEqualToString:@"AddViewController"])
    {
        // 딕셔너리를 추가.
        NSMutableDictionary *addData = [NSMutableDictionary dictionary];
       
        addData[@"name"] = @"Hong gil dong";
        addData[@"adddate"] = [NSDate date];
        [self.datas addObject:addData];


        // 네비게이션 컨트롤로 넘겨 줄때.
        UINavigationController *navigationController = segue.destinationViewController;
       
        AddViewController *addViewController = (AddViewController *)navigationController.topViewController;
        addViewController.data = addData;
    }

}

반응형
Posted by 컴스터
,
반응형


1. Default.png 및 Default@2x.png: 이 이미지는 구동 이미지다. 전체 화면 앱이라면 Default.png의 크기는 320 * 480 픽셀, Default@2x.png는 640 * 960 픽셀이 돼야 한다. 
상태바를 표시할려면 구동 이미지의 높이에서 20포인트를 제거해야 한다. 따라서 Default.png는 320 * 460 픽셀, Default@2x.png는 640 * 920 픽셀이 돼야한다.

2. Default-568h@2x.png: 아이폰 5 디스플레이를 위한 구동 이미지다. 아이폰 5는 모두 레티나 디스플레이를 지원하므로 이 이미지의 크기는 640 * 1136이 돼야 한다.
이 파일을 포함시키지 않으면 iOS에서는 앱이 아이폰5 화면을 지원하지 않는다고 가정해 앱 레터박스(화면 상단과 하단의 검은 바)를 표시한다.

3. Icon.png 및 Icon@2x.png: 아이폰 홈 화면에 보여줄 앱의 아이콘 PNG다. Icon.png는 57 * 57 픽셀, Icon@2x.png는 114 * 114픽셀이 돼야 한다.

4. Icon-72.png 및 Icon-72@2x.png: 이 아이콘은(애플리케이션이 아이폰 기기만을 대상으로 하더라도)아이폰 앱을 설치한 아이패드 사용자를 위한 아이콘이다.
이 아이콘 버전을 포함시키지 않으면 기본 57 * 57 아이폰 아이콘이 사용된다(이로 인해 아이패드에서 크기 조절에 따른 픽셀뭉개짐이 일어난다).
Icon-72.png는 72 * 72픽셀이어야 한다. 이 아이콘은 비레티나 아이패드에서 사용한다. Icon-72@2x.png는 114 * 114 픽셀 크기이어야 하면 레티나 아이패드에서 사용한다.

5. Icon-Small.png 및 Icon-Small@2x.png: 이 아이콘은 iOS 설정, 알림 화면, 스포트라이트 검색에 사용된다. 마찬가지로 이 아이콘도 포함시키지 않으면 iOS는 메인 57 * 57 아이콘을 사용해 29 * 29 포인트로 크기를 조절한다.

반응형
Posted by 컴스터
,
반응형

Ray로 부터 알림: 축하한다. 여러분들이 해냈다. iOS 6 Feast 단어를 널리 알리는 것을 도와줘서, 첫번째 iOS 6 튜토리얼을 미리 공개하게 되었다!

이 튜토리얼은 새로운 책인 iOS 6 튜토리얼의 하나의 챕터의 축약된 버젼이다. Matthijs Hollemans가 작성하였다. 그는 iOS Apprentice Series를 작성한 사람이다. 재미있게 즐겨라!

이 포스트는 iOS 튜토리얼 팀 맴버이면서 iOS 개발자와 디자이너의 경험이 있는 Matthijs Hollemans가 작성하였다.

이 튜토리얼의 파트 1에서 이전 방식의 “스트러츠-스프링” 모델으로 유저 인터페이스로 만들면 모든 레이아웃 문제를 쉽게 해결 할 수 없는 것을 보았다. iOS 6의 강력한 기술인 오토 레이아웃은 해결책을 제시한다.

이 튜토리얼 시리즈의 마지막은 두번째 파트에서 계속해서 제약조건의 모든 것과 어떻게 그것을 적용 할지에 대해 배울 것이다.

볼트체의 사용자 제약조건

캔버스에서 T자 모양의 바의 몇개는 다른 것보다 굵은 것을 아마도 알았을 것이다. 굵은 것들은 사용자 제약조건이라고 불린다. 그리고 얇은거와는 달리 그것을 삭제 할 수 있다. 그러나 사용자 제약조건이 삭제되면 인터페이스 빌더는 지울수 없는 제약조건을 그 자리에 삽입한다. 왜 그런지 잠시후에 볼 수 있다.

Document Outline에서 사용자 제약조건은 파란색 아이콘을 가진다.

도큐먼트 아웃라인에서 사용자 제약조건

Vertical Space (40) 제약조건을 선택하고 키보드에서 Delete 키를 누른다. 두 버튼 사이의 T자-바가 사라진다. 그리고 새로운 Vertical Space 제약조건으로 대체된다.

사용자 제약조건을 삭제 후

새로운 제약조건은 자주빛 아이콘을 가지고 굵은 선을 가지지 않는다. 이것은 지울 수 없다는 것을 의미한다. 두 개의 버튼은 더 이상 수직적으로 연결되지 않는다. 비록 두 개의 버튼은 Leading Alignment 제약조건 때문에 여전히 왼쪽 정렬된 상태이다.

왜 이런 일이 일어 나는가? 왜 인터페이스 빌더는 새로운 Vertical Constraint를 버튼에 덛붙이는가? 겨우 이와 같은 제약조건을 삭제 하라고 해서? 답은 이것이다.

각각의 뷰는 거기에 그것의 위치와 크기를 결정하기 위해 충분한 제약조건이 있어야 한다.

저것은 매우 오토 레이아웃을 사용 할 때 기억해야 할 가장 중요한 법칙이다. 제약조건이 충분하지 않다면, 오토레이 아웃은 뷰의 위치나 크기를 계산하지 못 할 것이다. 이러한 레이아웃은 무효로 간주된다. 이런 무효한 레이아웃의 예를 나중에 볼 수 있다.

인터페이스 빌더는 무효한 레이아웃을 만드는 것을 방지하기 위해 노력을 한다. 버튼이 자신의 크기가 얼마나 되야 적당한지를 자신의 텍스트, 배경 이미지 등을 기반으로 알고 있기 때문에 두 개의 버튼의 크기는 알려져 있다. 그러면 문제가 없다. 또한 위쪽 버튼의 X값은 아래쪽 버튼에 그것의 왼쪽 가장자리가 정렬되어 있고 아래쪽 버튼은 항상 수평적으로 가운데에 있기 때문에 알려져 있다. 오직 모르는 것은 Y값이다.

이전에는 Vertical Space로 두 개의 버튼이 연결되어 있었다. 그것은 위쪽 버튼의 Y 위치를 결정하는데 충분하다. 그러나 Vertical Space를 삭제하면, 위쪽 버튼은 뷰의 수직적으로 아무것도 고정하지 않는다. 오토 레이아웃은 Y 좌표가 어디에 있어야할지를 결정할 수 있는 방법이 없기 때문에 거기에 고정시킬수 없다.

이와 같은 문제를 방지 하려면 인터페이스 빌더는 아래쪽 가장자리의 가장 가까운 곳에 버튼을 “고정” 시켜야 한다.

Pin all the buttons

앱을 실행하고 가로 방향으로 회전시키면, 우습기는 하지만 여전히 작동하는 것 같다. 화면은 이전에 했던 것처럼 동일하게 보인다. 그것은 사실이나, 디자인은 근본적으로 다르다. 아래 버튼은 움직이나 위 버튼은 여전히 움직이지 않는 것을 의미한다. 두 해결책은 괜찮다. 앱에 무엇을 하고 싶은지에 따라 달라진다. 예를 들면 두 개의 버튼사이에 수직적인 연결을 가지고 한다.

이 설명을 하기 위해 아래쪽 버튼과 화면의 가장자리의 사이의 Vertical Space 제약조건을 선택한다. Attribute 인스팩터로 이동한다. 그것의 Constant는 현재 “Auto”로 읽는다. 그리고 Standard는 표준 여백 공간이므로 체크되어 있다. 40으로 변경한다.

버튼이 연결되지 않았기 때문에, 오직 아래쪽 버튼이 위로 이동한다. 위쪽 버튼은 머물러 있다.

Only bottom button moves up

제약조건의 Constant 값을 변경하면 그것이 “user” 제약조건으로 볼드화되여 변경 되는 것을 확인할 수 있다.

바늘과 핀

두 개의 버튼을 다시 연결해 보자. 캔버스에 버튼을 드래그해서 제약조건을 만들 뿐만 아니라 그것들을 나중에 만들 수도 있다. Cmd키를 누르고 두 개의 버튼을 클릭해서 선택한다. Editor 메뉴에서 PinVertical Spacing을 선택한다.

이 제약조건을 만들기 위해 하단의 오른쪽 구석의 작은 페널을 사용 할 수도 있다.

핀 메뉴 단축키

다음과 같이 메뉴가 팝업된다.

핀 메뉴

선택한 방법에 상관없이 두 개의 버튼 사이에 새로운 제약조건 추가한다.

수직 공간에 핀

20 포인트를 가진 새로운 Vertical Space 제약조건이다. 이 연결을 만든 때에 두 버튼의 사이는 20포인트의 거리를 두고 있기 때문이다.

위쪽 버튼부터 아래쪽 가장자리의 이전 Vertical Space는 아직 있다는 것을 확인한다. Vertical Space (104) 이 제약조건은 더 이상 필요 없으므로 삭제한다.

이전에 파란색 제약조건을 삭제할때 보라색이 대체 되었다. 나머지 제약조건은 모든 뷰를 배치하기 충분하기 때문에 지금은 그런게 없다.

다음의 제약조건을 가지고 있어야 한다.

복구된 제약조건

캔버스에서 클릭하여 아래 Vertical Space를 선택한다. 그리고 40에서 표준으로 그것의 Constant를 변경한다. 다시 버튼들이 연결되어 있기 때문에 아래 버튼을 아래쪽으로 이동하면 안되고 위 버튼도 마찬가지다.

작은 런타임 여행

이제 기본적인 것을 살펴 봤다. 가이드를 사용하여 콘트롤을 어떻게 배치하는지, 다른 콘트롤러 사이를 어떻게 정렬하는지 그리고 콘트롤 사이에 공간을 어떻게 넣는지를 알 것이다. 이 튜토리얼 과정을 통해서 Align과 Pin 메뉴로 다른 옵션을 사용 할 것이다.

인터페이스 빌더에서 다룬 것은 잘 작동하고 좋아 보인다. 그러나 실행시에는 이 작업이 어떤지 살펴보자. ViewController.m에 다음의 메서드를 추가한다.

- (IBAction)buttonTapped:(UIButton *)sender
{
   if ([[sender titleForState:UIControlStateNormal] isEqualToString:@"X"])
       [sender setTitle:@"A very long title for this button"
               forState:UIControlStateNormal];
   else
       [sender setTitle:@"X" forState:UIControlStateNormal];
}

버튼의 긴 제목과 짧은 제목을 전환한다. 인터페이스 빌더에서 두 개의 버튼과 엑션 메서드를 연결한다. 각각의 버튼을 콘트롤을 누르고 File’s Owner로 드래그 한다. 팝업에서 buttonTapped:을 선택한다.

앱을 실행하고 작동을 확인하기 버튼을 탭한다. 가로방향과 세로방향에서 테스트를 수행한다.

길고 짧은 타이틀

긴 타이틀과 짧은 타이틀에 상관없이 레이아웃은 주어진 제약조건을 항상 만족한다.

  • 아래 버튼은 윈도우에서 수평적으로 항상 가운데 정렬이다.
  • 아래 버튼은 윈도우 아래로 부터 20포인트에 위치한다.
  • 위 버튼은 아래 버튼에 왼쪽 정렬을 한다.

그것은 유저 인터페이스의 전체 사양이다.

재미를 위해 인터페이스 빌더에서 두 개의 버튼을 선택하고 Align 메뉴에서 Right Edges 선택한다. 앱을 다시 실행하고 달라진 것을 확인한다.

반복하지만 지금은 AlignHorizontal Centers를 선택한다. 그것은 아래 버튼을 고려해서 위 버튼을 항상 가운데 위치한다. 앱을 실행하고 버튼을 탭할때 버튼이 어떻게 반응하는지 봐라.

넓이 고정

핀(Pin)메뉴는 Widths Equally 옵션을 가진다. 두 개의 뷰에 이 제약조건을 설정하면, 오토 레이아웃은 두 개의 뷰를 큰 뷰를 기준으로 항상 같은 넓이로 만든다. 잠깐 가지고 놀아보자.

두 개의 버튼을 선택하고 PinWidths Equally을 선택한다. 두 개의 버튼에 새로운 제약조건을 추가 했다.

Buttons widths equally

메모: 두 개의 버튼 중 하나와 슈퍼뷰 사이에서 여분의 불필요한(unintended) 제약조건을 얻을려면, 두 개의 버튼을 선택하고 AlignHorizontal Center를 다시 선택하라.

이 튜토리얼의 첫번째 파트에서 이런 제약조건의 유형을 이전에 보았다. 이것은 평범한 T자 막대기로 보이지만 등호(=)를 가진 원이 중간에 있다.

Document Outline에서 하나의 Equal Widths 제약조건으로 나타난다.

Document Outline에서 Equal widths

하나의 버튼의 레이블 텍스트를 변경하면 다른 하나의 버튼의 크기도 변경 될 것이다.

아래 버튼의 레이블을 가장 작은 크기로 만들기 위해서 “X”로 변경한다. 위쪽 버튼이 더이상 텍스트의 크기에 맞지 않는 것을 볼 수 있다.

위 버튼의 텍스트는 더이상 맞지 않는다

인터페이스 빌더는 두 개의 버튼에 사용하는수 있는 크기를 어떻게 알 수 있을까? 주의깊게 보면 Width 제약조건이 텍스트가 잘린 버튼에 추가 된 것을 볼 수 있다.

Width constraint on truncated button in document outline

Width constraint on truncated button

인터페이스 빌더는 Equal Widths 제약조건을 따르기 위해 이상적인 조건 보다 강제적으로 버튼을 작게 한다.

분명히 이것은 원하는것이 아니다. 그래서 위쪽 버튼을 선택하고 Editor에서 Size to Fit Content를 선택한다. 또는 Cmd =를 누른다. 다시 텍스트의 크기는 버튼에 딱맞게 되었거나 버튼이 텍스트 주변에 딱 맞게 되었다. 그리고 Width 제약조건은 사라졌다.

앱을 실행하고 버튼을 탭한다. 가장 긴 레이블에 관계 없이 버튼은 항상 같은 넓이를 가진다.

Buttons equal widths in app

물론 두 개의 레이블이 매우 짧을 때도 두 개의 버튼은 동일하게 줄어든다. 방지하는 제약조건이 있지 않지만, 버튼은 더 크지도, 더 작지도 않게 컨텐츠의 크기에 맞게 크기가 꼭 맞게 된다. 무엇이라고 불렸는가? 고유 컨텐츠 크기이다.

고유한 콘텐츠 크기

오토 레이아웃 전에는 인터페이스 빌더에서 그것의 프레임 또는 경계 속성을 설정할 때, 그것들의 크기를 조정할 때는 버튼이나 다른 컨트롤러에 그것들의 크기를 항상 설정했다. 그러나 대부분의 콘트롤러는 콘텐츠를 기반으로 필요하는 공간의 양을 완벽하게 결정했다.

레이블은 텍스트의 폰트 크기와 텍스트의 길이를 알고 있기 때문에 넓이와 높이를 알 수 있다. 배경이미지와 둥근모서리를 위한 패딩을 가진 텍스트 결합한 버튼도 마찬가지다.

비록 일부는 높이는 정해져있고 너비는 알수 없다고 하지만, 세그먼트 콘트롤, 프로그레스 바, 그리고 대부분의 다른 컨트롤에도 동일하게 적용된다.

이것은 고유한 컨텐츠 크기로 알려져 있다. 그리고 이것은 오토 레이아웃의 중요한 컨셉이다. 버튼에서 이런 액션을 보았을 것이다. 오토 레이아웃은 컨트롤러에 필요한 크기를 요청하고 정보에 따라 화면을 배치 한다.

컨트롤에 명시적인 Width 또는 Height 제약조건을 설정하여 이 문제를 방지 할 수 있다. 손으로 컨트롤의 크기를 조정할 경우, 인터페이스 빌더는 이러한 명시적 제약조건을 설정한다. Size to Fit Content 명령어로 모든 고정된 Width 또는 Height 제약조건을 제거 할 수 있고, 콘트롤은 다시 고유 컨텐츠 크기를 결정 할 수 있다.

보통 고유 컨텐츠 크기를 사용을 원하지만, 원하지 않는 몇 가지 경우가 있다. 이미지가 화면보다 훨씬 큰 경우 UIImageView에 이미지를 설정할 때 어떤 일이 벌어지는 지 상상하라. 이미지의 크기로 뷰의 크기를 조정하는 것을 원하지 않으면, 이미지 뷰에 고정된 너비와 높이를 주고 컨텐츠를 확장한다.

그래서 버튼중 하나가 고정 Width 제약조건을 가졌을때 무엇이 발생하나? 버튼은 자신의 크기를 계산하지만 고정 넓이를 제공하여 오버라이드 할 수 있다. 위 버튼을 선택하고 PinWidth를 메뉴에서 선택한다. T자 막대기를 버튼 밑에 추가한다.

고정 넓이 제약조건을 가진 버튼

버튼의 슈퍼뷰가 아니라 버튼 자체에 이러한 제약조건 종류가 적용되기 때문에, Document Outline의 버튼 오브젝트 아래에 표시된다. 이 경우에는 버튼이 73포인트의 고정 넓이를 가졌다.

앱을 실행하고 버튼을 탭한다. 무엇이 일어나는가? 버튼의 텍스트는 변경되지만 충분한 공간이 없어서 텍스트가 잘린다.

잘린 버튼 텍스트

위쪽 버튼이 고정 넓이 제약조건을 가졌고 버튼은 동일한 크기를 요구하고 있기때문에 그것들은 절대 줄어들거나 늘어날수 없다.

메모: 버튼에 Width 제약조건을 아마도 설정하지 않을 수도 있다. 버튼의 고유 크기를 사용하는 것이 가장 좋다. 그러나 인터페이스 빌더가 하지 않고 컨트롤러의 싸이즈를 변경을 예상하는 곳의 레이아웃 문제를 실행하면, 인터페이스 빌더가 고정된 Width 제약조건을 모르게 설정하지 않도록 두 번 체크 하라.

뷰의 고정(Pin)과 정렬을 지겨워질때까지 가지고 놀아봐라. 처음부터 바로 알 수 없으니 점점 자유롭게 다룰 수 있을 것이다. 오토 레이아웃은 모든 뷰의 위치와 크기를 결정할 수 있도록 충분한 제약조건이 있다는 것만 명심하면 된다.

Got enough constraints

예제 갤러리

제약조건이란 무엇인지와 다른 뷰 사이의 강제적인 관계에 의해 레이아웃이 만들어지는 방법의 아이디어를 가지고 있어야 한다. 다음 섹션에서는 오토 레이아웃을 사용하는 방법과 실제 시나리오를 만족하는 레이아웃을 만드는 제약조건을 살펴 볼 것이다.

좋아하는 프로그래머의 갤러리를 가진 앱을 만들기를 가장하자. 가로 방향과 세로 방향에서는 다음과 같이 보인다.

갤러리 앱

화면은 4개의 똑같은 크기로 나눠져 있다. 각각은 이미지 뷰와 레이블을 가지고 있다. 어떻게 접근할까?

기본 앱을 설정으로 시작해보자. 기존의 “Constrants” 앱을 버튼을 삭제하고 뷰를 재사용해서 사용 할 수 있다.

또는 Single View Application 템플릿을 사용해서 새로운 프로젝트를 만들수 있다. 그리고 이름을 “Gallery”와 같이 마음에 드는 이름을 부여한다. nib만 사용 할 것이고 스토리보드 옵션을 해제한다.

ViewController.xib을 연다. Object Library에서 기본 뷰 오브젝트를 캔버스로 드래그 한다. 160 x 230 포인트의 크기로 조절한다. 그리고 배경색을 하얀색을 제외하고 바꾼다. 여기서는 녹색으로 했다.

오토레이 아웃 뷰

이 뷰는 위치를 유지하기 위한 4개의 제약조건이 있다. 버튼이나 레이블과는 달리 기본 UIView는 고유 콘텐츠 크기를 가지고 있지 않는다. 각각의 뷰의 위치와 크기를 결정하는 충분한 제약조건이 항상 있다. 그래서 이 뷰는 필요로 하는 크기가 무엇인지 제약조건에 알려주어야 한다.

이런 크기 제약조건이 어디에 있는지 궁금할 수 있다. 이 경우에는 뷰의 크기는 슈퍼뷰의 크기에 의해 암시된다. 이 레이아웃의 제약조건은 두 개의 Horizontal Space와 두 개의 Vertical Space 이다. 그리고 모두 고정 넓이를 가진다. Document Outline에서 이와 같이 볼 수 있다.

Document Outline에서 UIView의 제약조건

녹색 뷰의 넓이는 “슈퍼뷰의 넓이 빼기 (109 + 51)” 공식에의해 계산된다. 높이는 “슈퍼뷰의 높이 빼기 (153 + 77)” 공식에 의해 계산된다. 공간 제약조건은 고정된다. 그래서 뷰는 크기조정은 되나 선택은 안된다. 앱을 화면전환 했을 때, 슈퍼뷰의 크기는 320×640에서 480×300으로 변경된다. 이 새로운 너비와 높이가 공식으로 변환되어 녹색 뷰의 새로운 크기를 얻을 수 있다.

앱을 실행하고 가로화면으로 회전할 때 다음과 같이 볼 수 있다. 그러나 인터페이스 빌더에서 직접적으로 그것을 시뮬레이션 할 수 있다.

nib에서 맨 위쪽 뷰를 선택하고 Attributes 인스펙터로 이동한다. Simulated Metrics 섹션 밑에 Orientation을 Landscape로 변경한다.

Simulated metrics landscape

이렇게 하면 가로 방향에서 nib의 레이아웃을 어떻게 보이는지 간단하게 미리보기를 할 수 있다. 녹색 뷰는 Horizontal과 Vertical Space 제약조건에 만족하기 위해 크기가 조정된다.

다시 세로 화면으로 전환 한다.

메모: nib으로 평범한 UIView를 드롭하는 두가지 메인 이유가 있다. 1) nib의 컨텐츠를 정리하는 것을 돕기 위한 다른 뷰의 컨테이너로 사용 한다. 2) 커스텀 뷰 또는 컨트롤러의 플레이스 홀더로 사용하고 Class 속성을 UIView 또는 UIControl 서브클래스의 이름으로 설정 할 수 있다.

기기 회전을 할때 항상 UIView의 크기 조절을 원하지 않을 수 있다. 그래서 뷰에 고정된 너비 그리고/또는 높이를 주기위해 제약조건을 사용 할 수 있다. 녹색 뷰를 선택하고 Pin 메뉴에서 Width를 선택한다. 뷰를 다시 선택하고 PinHeight를 선택한다.

뷰에 두 개의 제약조건(160 포인트의 Width 제약조건과 230 포인트의 Height 제약조건)을 추가 했다.

UIView의 Width와Height 제약조건

Width와 Height를 이 뷰에 적용하기 때문에 Documents Outline에 View아래에 그것들이 위치한다. 예를 들면 Horizontal과 Vertical Space 제약조건은 녹색 뷰와 그것의 슈퍼뷰인 회색 뷰 처럼, 일반적이지 않게 제약조건은 두 개의 다른 뷰를 표현하지만 뷰와 자체 사이의 관계로 Width와 Height 제약조건을 고려 할 수 있다.

앱을 실행한다. 세로 방향에서도 보기 좋다. 다시 가로 방향으로 회전한다. 원하는 것 처럼 되지 않았을 뿐만 아니라 Xcode 디버그 창에 심각한 오류 메세지가 나타났다.

Gallery[68932:11303] Unable to simultaneously satisfy constraints.
	Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
   "<NSLayoutConstraint:0x754dac0 V:[UIView:0x754e510(230)]>",
   "<NSLayoutConstraint:0x754eac0 V:|-(77)-[UIView:0x754e510]   (Names: '|':UIView:0x754e3a0 )>",
   "<NSLayoutConstraint:0x754ea40 V:[UIView:0x754e510]-(153)-|   (Names: '|':UIView:0x754e3a0 )>",
   "<NSAutoresizingMaskLayoutConstraint:0x7558cd0 h=-&- v=-&- UIView:0x754e3a0.width == UIWindow:0x71156e0.width - 20>",
   "<NSAutoresizingMaskLayoutConstraint:0x74128b0 h=--- v=--- H:[UIWindow:0x71156e0(320)]>"
)
 
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x754dac0 V:[UIView:0x754e510(230)]>
 
Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

충분한 제약조건이 있어야 오토 레이아웃이 모든 뷰의 위치와 크기를 계산할 수 있다고 말한 것을 기억하는가? 이 예제에는 너무 많은 제약 조건이 있다. “Unable to simultaneously satisfy constraints” 오류를 발생 할 때마다, 어디선가 제약조건이 충돌이 난다는 것을 의미한다.

다시 제약조건을 살펴 보자.

충돌난 제약조건

녹색 뷰에는 6개의 제약조건이 설정되어 있다. 이전에 봤듯이 4개의 Space 제약조건과 그것에 설정한 2개의 새로운 Width와 Height 제약조건이다. 그래서 어디에서 충돌이 일어났나?

세로 방향 모드에서는 계산이 추가 되었기 때문에 문제가 되지 않는다. 슈퍼뷰의 너비는 320 포인트이다. Horizontal Space 제약조건의 너비와 뷰의 너비를 추가하는 경우 320으로 설정되어야 한다. 뷰를 배치한 방법은 실제로 51 + 160 + 109 = 320 이다. 마찬가지로 수직 제약 조건은 460까지 추가해야 한다.

그러나 기기를 가로 방향으로 회전했을 때, 윈도우는 490 포인트 너비를 가진다. 그 뜻은 51 + 160 + 109 + ? = 480 이다. 방정식과 오토 레이아웃이 그것들을 어디에 위치시킬지 모르는 것을 위해 필요한 160이 여분의 포인트가 있다. 수직축도 마찬가지다.

여기 충돌은 뷰의 너비가 고정되어 있으며 하나의 마진은 유동적이여야 한다, 또는 마진은 고정되어 있고 너비는 유동적이여야 한다는 것이다. 그래서 이런 제약조건중 하나가 실행되었다. 위의 예제에서는 가로와 세로 방향 모두에서 뷰가 같은 너비를 가지기를 원했다 그래서 Horizontal Space 진행됐다.

오른쪽의 Horizontal Space와 아래쪽의 Vertical Space를 제거한다. nib은 다음과 같을 것이다.

충돌 제약조건 수정

이제 뷰는 크기와 위치를 결정하는 더 많지도 적지도 않은 적당한 수의 제약조건을 가지고 있다. 앱을 실행하고 오류 메세지가 사라진 것과 화면 회전후 뷰가 같은 크기를 유지하는지 확인하라.

메모e: 인터페이스 빌더가 잘못된 레이아웃을 방지하기 위해 최선의 노력을 하지만, 그것은 기적을 수행 할 수는 없다. 적어도 오토 레이아웃은 뭔가 잘못되었을 때 자세한 오류를 뱉는다. iOS 6 튜토리얼의 “중금 오토 레이아웃”에서 이런 에러 메세지를 진단하고 오류 메세지를 분석하는 것을 배운다.

세로 방향 색칠하기

녹색 뷰에 두 개의 레이블을 드래그 한다. 녹색 뷰가 레이블의 슈퍼뷰이기 때문에 녹색 뷰에 가이드가 나타난다.

녹색 뷰에 레이블 드래기 하기

가이드에 대한 왼쪽 위의 구석에 레이블을 배치한다. 녹색 뷰의 왼쪽 위 구석에 레이블을 고정하는 두 개의 Space 제약조건을 추가한다.

레이블 제약조건

매인 뷰가 아니라, 녹색 뷰의 제약조건 섹션 밑에 두 개의 새로운 Horizontal과 Vertical Space 제약조건이 리스트되는 것을 확인할 수 있다.

녹색 뷰로 이동해 보자. 녹색 뷰와 그것의 슈퍼뷰의 사이의 유일한 제약조건의 변경을 볼 수 있다. 레이블은 항상 녹색 뷰와 상대적으로 같은 위치에 위치해야 한다.

레이블을 선택하고 아래 마진, 수평적으로 가운데에 위치시킨다. 그리고 새로운 이미지 뷰 오브젝트를 nib에 드래그 한다. 다음과 같은 레이아웃이 만들어 질 것이다.

Image view in gallery

이미지 뷰는 슈퍼뷰의 위, 왼쪽과 오른쪽 가장자리에 고정된다. 그러나 아래는 표준 간격으로 레이블의 상단과 연결되어 있다.

이 튜토리얼의 리소스를 다운로드 받고 압축을 푼다. Images 폴더를 프로젝트에 추가하라. Ray.png 파일을 이미지 뷰의 이미지로 설정한다. 이미지뷰의 모드를 Aspect Fit으로 변경하고 배경색으로 하얀색으로 설정한다. 레이블의 텍스트를 “Ray”로 변경한다.

레이아웃은 다음과 비슷할 것이다.

Ray의 갤러리

이제 인터페이스 빌더는 Height 제약조건을 배치한것을 확인 할 수 있다. 이미지 뷰에 이미지를 설정 한 순간에 일어났다.

높이가 고정된 레이블

인터페이스 빌더는 모호한 레이아웃으로 알려진 것을 방지하기 위해 시도 한다. 이미지 뷰나 레이블이 고정된 높이를 가지고 있다면, 오토 레이아웃은 녹색 뷰의 높이를 변경해야 할 각각의 크기를 조정하는 방법을 알지 못한다. (인터페이스 빌더는 이제 녹색의 뷰가 실제로 거기에 설정할 고정된 Height 제약사항을 가지는 것을 무시하는 것 같다.)

앱의 그린뷰의 어떤 포인트가 100 포인트 커진다고 가정해보자. 오토 레이아웃은 새로운 100 포인트를 레이블과 이미지 뷰 중에 어디에 어떻게 배포를 해야할까? 레이블이 같은 크기로 있는 동안에 이미지 뷰가 100 포인트 커져야 할까? 아니면 이미지 뷰가 같이 크기로 있는 동안에 레이블이 100 포인트 켜져야 할까? 각각 50 포인트 또는 25/75, 40/60으로 나눠야 할까? 다른 가능한 방법으로 조합해야 할까?

오토 레이아웃은 추측할 수 없다. 그래서 인터페이스 빌더가 레이블에 고정된 높이를 줘서 이 문제를 “해결” 한다. 또한 이미지 뷰에 고정된 높이를 줄 수 있지만 레이블에 주는 것이 더 의미가 있다.

지금은 레이블에 Height 제약조건을 주도록 하자.

메모: 이 문제에 대한 적절한 해결방법은 레이블의 “Content Compression Resistance Priority”를 변경하는 것이다. 이것에 대해서 나중에 배울 것이다. 기다릴 수 없다면, 레이블의 Size 인스펙터로 이동해서 Vertical Content Compression Resistance Priority를 751로 설정해라. 레이블에 Height 제약조건은 이제 사라진다.

다른 헤드를 추가하기

메인 뷰의 왼쪽-위 구석으로 녹색 뷰를 옮긴다. 녹색 뷰는 부모 뷰에서 그것의 위치를 결정하는 Horizontal Space와 Vertical Space 제약조건을 가진다고 상기하자. 여전히 그것들을 가지고 있지만 지금은 값을 0으로 설정하자. 윈도우의 위와 왼쪽 가장자리에 하얀색 테투리를 가진 굵은 파란색 선으로 표시된다.

왼쪽-위 구석의 뷰

그래서 비록 뷰가 완벽하게 구석에 있더라도, 여전히 거기에 고정 시킬 제약조건이 필요하다. 0값을 가진 마진으로 생각하라.

녹색 뷰를 선택하고 Cmd-D를 눌러서 복사하라. 복사된 것을 오른쪽-위 구석으로 옮겨라.

오른쪽-위 구석의 녹색 뷰

2개 더 복사하고 이것들을 왼쪽-아래와 오른쪽-아래 구석에 각각 이동한다.

화면 디자인은 다음과 같이 변경한다.

갤러리 디자인

이것들은 잘생긴 프로그래머들이다! :-)

앱을 실행한다. 세로 방향에서는 괜찮게 보이지만 가로 방향에서는 그렇지 않다.

가로 방향에서 잘못된 갤러리

뭔가 잘못된 것이 분명하다. 고정된 너비와 높이를 뷰에 설정했다. 그래서 그것들은 슈퍼뷰의 크기에 관계 없이 항상 저런 크기를 가진다.

모든 4개의 뷰에서 Width (160)과 Height (230) 제약조건을 선택한다 그리고 그것들을 삭제한다. 앱을 다시 실행하면 다음처럼 보일 것이고 이것 또한 좋아 보이지는 않는다/

가로 방향에서 여전이 이상하게 보임

초반에 우리가 해결한 문제와 매우 비슷하게 보인다. 그래서 어떻게 해결했는지를 생각하면 뷰에 평등한 너비와 높이를 준 것을 기억 할 수 있다.

4개의 뷰를 선택하고 PinWidths Equally를 선택한다. 뷰를 다시 선택하고 PinHeights Equally를 선택하라.

앱을 다시 실행하고 기기를 회전하라. 흠… 여전히 전과 정확하게 똑같이 보인다. 왜?

스크린 샷을 보면 모든 뷰는 똑같은 높이를 가지고 있는 것을 볼 수 있다. 또한 똑같은 너비를 가지고 있다. (녹색과 갈색 뷰는 노란색과 파란색 뷰 때문에 부분적으로 보인다). 그래서 제약조건이 충족되고 있다. 이것은 원했던 너비와 높이가 아니다. 해결 방법을 얻기 위한 다른 제약조건이 있어야 한다.

이런 뷰의 제약조건을 보면, 그것들을 강제적으로 배치하게 하는 Horizontal과 Verticdal Space 제약조건을 가지고 있는 것을 볼 수 있다. 4내의 서브뷰가 아니라 메인 뷰의 제약조건 리스트를 보아라.

잘못된 Horizontal Space

더 나쁜것은 그런 제약조건을 삭제 할 수 없다. T자 막대기는 굵게되지 않고 제약조건은 파란색이 아니다. 그래서 인터페이스 빌더는 레이아웃 문제를 방지하기 위해서 거기에 그것을 넣었다.

오토 레이아웃은 4개의 뷰가 각각 어떻게 연결되어 있는지를 알 수가 없기 때문에, 4개의 뷰가 가져야 할 똑같은 크기는 그것들의 크기를 실제로 결정하기에 충분하지 않다. 그것들은 디자인에 나란히 표시되지만, 그것들 사이에 실제 제약조건은 없다. 오토 레이아웃은 “Ray”와 “Matthijs” 박스사이에서 윈도우 너비가 분할 할 필요가 있는지 알지 못한다.

오토 레이아웃은 스스로 알 수 없으면, 그것을 전달해야만 한다.

연결됨

Ray와 Matthijs 박스를 선택하고 PinHorizontal Spacing를 선택한다. 박스는 나란히 있기 때문에, 그것들 사이에 크기를 0으로 Horizontal Space 제약 조건 추가한다. 그리고 그것은 두 개의 뷰가 어떻게 연결되었는지 알기에 충분하다.

중요t: 인터페이스 빌더는 자동으로 슈퍼뷰와 노란색 박스 사이의 Horizontal Space를 제거하지 못한다. 그러나 그것을 사용자 제약조건(두꺼운 막대기)으로 만든다. 이제 이 공간을 삭제 할 수 있다. 만약 못한다면, 가로 방향으로 회전하는 동안에 “Unable to simultaneously satisfy constraints” 오류가 발생할 것이다.

앱을 실행하라. 다음과 같이 보일 것이다.

조금 괜찮아진 가로 방향

조금 나아 보인다. 4개의 박스는 동일한 너피를 가졌다. 그러나 높이는 여전히 잘못 되었다. 해결방법은 비슷하다. Ray와 Dennis Ritchie 박스 사이에 Vertical Space를 넣는다. 그리고 Dennis Ritchie와 윈도우 위 사이의 Vertical Space를 삭제한다.

앱을 다시 실행하라. 지금은 모든 것이 올바르게 되어 보인다.

올바른 가로 방향의 갤러리

이미지 뷰 아래의 “Dennis Ritchie” 레이블이 중앙에 있지 않다. 레이블의 텍스트를 입력 할 때 발생 했다. 레이블은 뷰의 초기에 중앙에 있지만, 인터페이스 빌더가 잘 알고나서 Horizontal Space로 중앙 제약조건을 교체했다. 이런 일이 생겼으면, 수정하기 위해 레이블을 선택하고 AlignHorizontal Center in Container을 선택하라.

이미지 뷰의 메모. 고정 크기를 주지 않았기 때문에 이미지 뷰는 늘어난다. 알고 있지 않았다면, 의도적인 한 부분이다. ☺ 이미지 뷰가 가로 방향에서 맞지 않을 것이다. 그러나 이미지 뷰가 원래 가로 세로 비율을 유지하기 위하면 운이 없을 것이다. 인터페이스 빌더를 사용해서는 다음과 같은 효과를 얻을 수 없다.

이미지 가로 세로 비율

불행하게도 현재 인터페이스 빌더는 뷰의 가로 세로 비율을 유지하는 제약조건을 만드는 방법을 제공하지 않는다. 이것을 하기 위해서는 프로그래밍 방식으로 제약조건을 만들고 설정해야 한다. iOS 6 튜토리얼의 “중급 오토 레이아웃” 에서 방법을 배울 것이다.

팁: Simulated Metrics아래의 Orientation 설정을 변경해서 UI를 가로 방향으로 보이게 미리보기를 할 수 있는 것을 보았다. 인터페이스 빌더에서 직접 뷰의 크기 변경 동작을 테스트 할 수 있다.

메인 뷰를 선택한다. Simulated Mertrics 아래에서 Size를 Freeform으로 설정한다. 원하는 어떤 모양으로 nib의 크기를 변경할 수 있게 만든다. 오토 레이아웃은 바로 레이아웃을 계산하여 보여준다.

Freeform

그러나 이것은 주의해라. 때때로 인터페이스 빌더는 크기를 변경할 때 새로운 제약조건을 추가한다. 여기에서는 오른쪽-아래에 Horizontal Space를 추가했다. nib의 경계가 벗어났을 때 기존의 제약조건을 삭제 할 수 있다.


반응형
Posted by 컴스터
,
반응형

메모 iOS Human Interface Guidelines의 축약인 “HIG”는 좋은 유저 인터페이스 디자인을 위해 애플이 추천하고 있는 것을 포함한다. iOS 개발자들이 필수 적으로 읽어야 한다. HIG 어떤 UI 요소가 어떤 상황에서 사용하기 적합하고 사용하기 가장 좋은 방법을 설명한다. 여기에서. 이 문서를 찾을 수 있다.

두 개의 콘트롤 사이의 표준 공간은 제한되지 않는다. 제약조건은 뷰 처럼 수정할 수 있는 객체이다. 따라서 변경할 수 있는 속성이 있다.

두 개의 버튼 사이의 Vertical Space 제약조건 선택한다. 비록 약간 신경이 쓰이게 까다롭지만 캔버스에서 T자 바를 클릭하면 이 작업을 할 수 있다. 가장 쉬운 방법은 Documents Outline에서 제약조건을 클릭하는 것이다. 일단 그것을 선택하면 Attribute 인스펙터로 전환된다.

V-space attributes

기본적으로 표준속성은 체크되어있다. 두 의 오브젝트 사이의 공간 제약사항은 8 포인트이다. 슈퍼뷰의 가장자리 주변 마진은 20 포인트이다. 얼마나 큰 제약조건인지 확인을 위해 Constant 필드에 40을 입력을 한다. 두개의 버튼은 좀 더 멀어졌다. 그러나 둘은 여전히 연결되어 있다.

V-space larger

앱을 실행하고 가로방향으로 회전해서 다음 효과를 확인한다.

V-space larger landscape

두 개의 버튼의 수직 정렬은 확실하게 유지되고 있다. 그러나 수평 정렬은 그렇지 않다.

nib을 보면 위쪽 버튼과 캔버스의 왼쪽 가장자리 사이의 Horizontal Space 가 보인다. (적어도 필자가 한 것과 같이 대략적으로 버튼을 배치한 경우)

H-space anchoring top button

아래 레이블은 화면에서 수평적으로 중앙에 있다. 그러나 위쪽 버튼은 그렇지 않다. 항상 왼쪽 가장자리로부터 똑같은 거리를 유지한다.

멋지게 보이지 않는다. 그래서 대신에 다음 의도를 표현 할 것이다.

“아래쪽 버튼은 항상 수평적으로 가운데 있을 것이다. 그리고 위쪽 버튼은 아래쪽 버튼의 왼쪽 가장자리에 그것의 왼쪽 가장자리가 정렬 될 것이다.”

첫 번째 조건으로 제약조건을 이미 가졌다. 그러나 두 번째는 아니다. 인터페이스 빌더는 정렬을 위한 가이드를 보여준다. 그래서 그것의 왼쪽 가장자리가 아래쪽 버튼의 왼쪽 가장자리에 자석효과로 붙을 때까지 드래그 할 수 있다.

Snap left edges

불행하게도 이 또한 두 버튼의 사이의 Vertical Space를 (적어도 가끔씩 드래그와 배치된 방법에 따라서) 삭제한다. 인터페이스 빌더는 그것이 거기에 있었고 뷰의 아래에 Vertical Space 포함해서 대체 하는 것을 간단하게 잊는다.

Two buttons left aligned, wrong V-space

이것은 원하는 것과 다르다. 모든 방법으로 윈도우의 가장자리가 확장 된 것이 아니고 대신 두 버튼 사에 Vertical Space가 있어야 한다. 이러한 일이 일어났을 때 느껴지는 느낌은 다음 만화와 같다.

이전에 언급 한 바와 같이 제약조건을 그대로 유지하길 원하면 뷰 주위에 드래그 하는 것은 좋은 생각이 아니다. 두 버튼의 정렬하기 위한 좋은 방법이 있다.

우선 위쪽 버튼을 원래의 정렬되지 않은 이전 상태로 돌려 놓는다. Cmd 키를 누르고 두 버튼을 선택하기 위해 클릭한다. Editor 메뉴에서 AlignLeft Edges을 선택한다. 이것은 인터페이스 빌더에게 기존의 제약조건은 그대로 두고, 두 개의 버튼을 왼쪽 정렬을 원하는 것을 알려준다.

Left aligned buttons.png

보는것 처럼 새로운 “Leading Alignment” 제약조건이 아래 버튼에 위쪽 버튼의 왼쪽 정렬을 유지하기 위해 추가될 동안에 이전 제약조건(아래 버튼의 Center X Alignment와 두 개 버튼 사이의 Vertical Space)은 여전히 존재한다.

팁: 정렬 옵션을 선택하기 위해 항상 Editor 메뉴로 갈 필요가 없다. 인터페이스 빌더는 오른쪽 아래 코너에 단축 메뉴를 가지고 있다.

단축 메뉴

가장 왼쪽 버튼은 정렬 메뉴를 연다.

정렬 메뉴

이 옵션을 많이 사용하기 때문에 단축 메뉴를 사용하면 시간이 많이 절약 될 것이다.

앱을 실행하고 가로방향으로 회전해서 잘 작동하는지 확인한다.

Left aligned buttons landscape

어디로 가야 하나?

이제 오토 레이아웃의 첫 맛을 보았다. 어떻게 마음에 드는가? 익숙하기까지 시간이 살짝 걸릴 수 있지만 삶을 편안하게 만들어 주고 앱도 더 유연하게 된다.

더 배우길 원하면 계속해서 이 튜토리얼의 파트 2를 읽어라. 오토 레이아웃이 제공하는 가능성과 발생할 수 있는 문제의 더 나은 이해를 얻기 위해 인터페이스 빌더에서 버튼으로 계속해서 작업 할 것이다.

그리고 무엇보다도 실제 앱에서 찾을 수 있는 현실적인 레이아웃을 만들기 위해 오토 레이아웃을 사용 할 것이다.

어떠한 질문이나 의견이 있으면 포럼의 토론에 참여하라.

반응형
Posted by 컴스터
,
반응형

Ray로 부터 알림: 축하한다. 여러분들이 이루어냈다. iOS 6 Feast 단어를 널리 알리는 것을 도와줘서, 첫번째 iOS 6 튜토리얼을 미리 공개하게 되었다!

이 튜토리얼은 새로운 책인 iOS 6 튜토리얼의 하나의 챕터의 축약된 버젼이다. Matthijs Hollemans가 작성하였다. 그는 iOS Apprentice Series를 작성한 사람이다. 재미있게 즐겨라!

이 포스트는 iOS 튜토리얼 팀 맴버이면서 iOS 개발자와 디자이너의 경험이 있는 Matthijs Hollemans가 작성하였다.

가로/새로 방향 모두에서 보기좋게 앱을 만들려고 하다가 좌절한 적이 있는가? iPhone과 iPad 둘 다 지원하는 화면 레이아웃을 만드는 것은 미치기 직전으로 만든다. 절망은 더 이상 없다. 여러분에게 좋은 소식을 가져왔다.

항상 같은 크기를 보증하는 화면을 위한 유저 인터페이스를 디자인 하는 것은 어렵지 않지만, 화면 프레임이 바뀌면 UI 구성요소의 위치, 크기 또한 이러한 새로운 장비의 크기에 맞게 변경되어야 한다.

지금까지 화면 디자인이 합리적으로 복잡했다면, 레이아웃 적용을 지원하기 위해 많은 코드를 작성해야 만 했다. 더 이상 이런 경우가 없다는 것이 다행 일 것이다. iOS 6는 iPhone과 iPad에 오토 레이아웃이라는 놀라운 새로운 기능을 제공한다.

오토 레이아웃은 앱에서 다른 화면 크기를 쉽게 지원하는 것을 만들어 줄 뿐만 아니라, 보너스로 간단하게 지역화를 만들게 지원한다. 지원하고 싶어하는 모든 언어를 위해서 새로운 nib이나 스토리보드를 만들지 않아도 된다. 그리고 히브리어와 아랍어와 같은 오른쪽에서 왼쪽으로 쓰는 언어도 포함해서 지원한다.

이 튜토리얼은 인터페이스 빌더를 사용하여 오토 레이아웃을 시작하는 방법을 보여준다. iOS 6 튜토리얼에서는 이 튜토리얼 보다 더욱더 추가되었고 지식으로 만드는 방법과 코드를 통해서 오토 레이아웃의 전체 능력을 발휘하는 방법을 보여주는 완전히 새로운 장을 갖추고 있다.

간식과 좋아하는 카페인 음료를 들고 오토 레이아웃 마스터가 될 준비를 해라!

스프링과 스트러츠의 문제점

“스프링과 스트러츠” 모델로 알려진 오토싸이즈 마스크를 잘 알고 있을 것이다. 오토싸이즈 마스크는 슈퍼뷰의 크기가 변경될 때 뷰에 어떤 일을 결정한다. 유연한 또는 고정 여백(스트러츠)를 가지고 있는가? 그리고 너비와 높이(스프링)에 어떤 일이 있는가?

예를들어 슈퍼뷰가 넓어지면, 유연한 너비를 가진 뷰는 비례적으로 넓어질 것이다. 그리고 고정된 오른쪽 여백을 가진 뷰의 오른쪽 가장자리는 항상 슈퍼뷰의 오른쪽 가장자리에 고정될 것이다.

오토싸이즈 시스템은 간단한 경우에 잘 동작 된다. 그러나 레아아웃이 더 복잡해질 때 제대로 동작하지 않는다. 스프링과 스트러츠가 단순하게 자르지 않는 예를 살펴보자.

Xcode를 실행하고 Single View Application 템플릿을 기반으로 새로운 프로젝트를 생성한다. 앱 이름을 “StrutsProblem” 으로 작성한다. iPhone을 선택하고 스프링보드를 비활성화 한다.

프로젝트 옵션

인터페이스 빌더에서 ViewController.xib를 클릭하여 연다. 어떤 작업을 하기 전에, 우선적으로 nib의 Auto Layout을 비활성화 한다. 이 작업은 File 인스펙터에서 한다.

오토레이아웃 비활성화

“Use Autolayout” 체크박스에 체크를 해제한다. 지금 nib은 예전 스트러츠-스프링 모델을 사용한다.

메모:Xcode 4.5 이상에서 생성된 nib이나 스토리보드 파일에서는 오토 레이아웃이 기본적으로 활성화 된다. 오토 레이아웃은 iOS 6 기능이기 때문이다. 만약 Xcode 4.5를 사용해서 iOS 5 앱을 만들기를 원한다면, nib이나 스토리보드 파일을 “Use Autolayout” 체크박스에 체크를 해제해서 비활성화 해야 한다.

3개의 뷰를 메인 뷰에 끌어다 놓고 다음과 같이 정렬하라.

세로 디자인

구별하기 위해서 각각의 뷰에 색상을 부여하여 어떤 뷰가 어떤 뷰인지 확인 할 수 있다.

각 뷰는 윈도우의 경계에 20 포인트를 준다. 뷰 사이의 패딩에도 20포인트를 준다. 아래의 뷰는 280 포인트 넓이를 주고, 위의 2개의 뷰는 각각 130 포인트 넓이를 준다. 모든 뷰는 200 포인트 높이를 준다.

앱을 실행하고 시뮬레이터를 회전 시킨다. 또는 디바이스를 가로로 회전 시킨다. 앱의 화면이 다음과 같이 나타날 것이다. 생각과는 많이 다르다.

잘못된 가로화면

메모: HardwareRotate Left 그리고 Rotate Right 메뉴 옵션을 사용하여 시뮬레이터를 회전 시킬 수 있다. 또는 Cmd 키를 누른상태에서 좌 또는 우 화살표 키를 누르면 된다.

대신에 가로화면은 다음과 같이 보이길 원했다.

올바른 가로화면

3개의 뷰의 오토싸이징 마스크에 명백하게 원하는 무언가를 조금 남겨야 한다. 왼쪽-위 뷰의 오토싸이징 설정을 다음과 같이 변경한다.

왼쪽-위 뷰의 오토싸이징

이것은 위와 왼쪽의 가장자리를 고정시킨다. 그러나 아래와 오른쪽 가장자리는 고정시키지 않는다. 그리고 슈퍼뷰의 크기가 변경이 될 때 가로/세로 크기 모두를 변경한다.

비슷하게 오른쪽-위 뷰의 오토싸이징 설정을 변경한다.

오른쪽-위 뷰의 오토싸이징

그리고 아래 뷰는 다음과 같이 변경한다.

아래 뷰의 오토싸이징

앱을 다시 실행하고 가로로 화면을 회전한다. 이제는 다음과 같이 보일 것이다.

잘못된 가로화면 (2)

뷰 사이의 패딩이 잘못되었다. 다른 방법으로 봐도 뷰의 크기가 100% 옳지 않다. 문제는 오토싸이징 마스크가 슈퍼뷰 크기가 변경이 되었을때 뷰에게 변경된 크기를 알려준다는 것이다. 그러나 뷰에게 얼만큼 뷰의 크기가 변경되어야 하는지 알려줄 방법이 없다.

오토싸이즈 마스크로 해결 할 수 있다. 예를 들면 유연하게 넓이와 높이 설정(스프링)을 변경한다. 그러나 3개의 뷰 사이에 20 포인트를 가지도록 완벽하도록 정확하게 보이게 할 수 없다.

 왜?!?!?

스프링과 스트러츠 방법으로 레이아웃 문제를 해결하기 위해서는 불행하게도 몇 줄의 코드를 작성해야 한다.

UIKit가 뷰 콘트롤러에게 유저 인터페이스가 회전하기 전, 회전하는 동안, 회전한 후에 몇 가지 메세지를 보낸다. 이런 메세지를 가로채서 UI 레이아웃을 변경 할 수 있다. 일반적으로 재배치를 원하는 뷰의 프레임을 변경을 하기 위해서는 willAnimateRotationToInterfaceOrientation:duration:을 오버라이드 한다.

그러나 이 작업을 하기 전에 먼저 배치하기 위한 뷰에게 전달할 아웃렛 프로퍼티를 만들어야 한다.

(Xcode 툴바의 Editor 툴셋의 가운데 버튼) Assistant Editor 모드로 전환한다. 3개의 뷰를 각각 ViewController.h:로 콘크롤-드래그 한다.

콘트롤-드래그 아웃렛 프로퍼티

각각 뷰와 3개의 프로퍼티를 연결한다.

@property (weak, nonatomic) IBOutlet UIView *topLeftView;
@property (weak, nonatomic) IBOutlet UIView *topRightView;
@property (weak, nonatomic) IBOutlet UIView *bottomView;

다음 코드를 ViewController.m에 추가한다.

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                                        duration:(NSTimeInterval)duration
{
   [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
 
   if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft
   ||  toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
   {
       CGRect rect = self.topLeftView.frame;
       rect.size.width = 210;
       rect.size.height = 120;
       self.topLeftView.frame = rect;
 
       rect = self.topRightView.frame;
       rect.origin.x = 250;
       rect.size.width = 210;
       rect.size.height = 120;
       self.topRightView.frame = rect;
 
       rect = self.bottomView.frame;
       rect.origin.y = 160;
       rect.size.width = 440;
       rect.size.height = 120;
       self.bottomView.frame = rect;
   }
   else
   {
       CGRect rect = self.topLeftView.frame;
       rect.size.width = 130;
       rect.size.height = 200;
       self.topLeftView.frame = rect;
 
       rect = self.topRightView.frame;
       rect.origin.x = 170;
       rect.size.width = 130;
       rect.size.height = 200;
       self.topRightView.frame = rect;
 
       rect = self.bottomView.frame;
       rect.origin.y = 240;
       rect.size.width = 280;
       rect.size.height = 200;
       self.bottomView.frame = rect;
   }
}

뷰 콘트롤러가 새로운 방향으로 회전하고 있을 때 이 콜백이 발생한다. 뷰 컨트롤러 방향이 회전할 때 뷰의 크기를 적절하게 변경한다. 이 상황에서는 아이폰의 알려진 화면 크기에 기초로 제공된 하드코딩으로 변경한다. 에니메이션 블록에서 이 콜백이 발생한다. 그래서 크기 변경 에니메이션이 실행된다.

앱을 아직 실행하지 마라. 먼저 다음과 같이 3개 뷰의 오토리싸이징 마스크를 복원해야 한다. 아니면 오토리싸이징 메카니즘은 뷰의 willAnimateRotation에 설정해 놓은 위치와 크기 때문에 크래쉬된다.

오토싸이징 끄기

그것을 해야한다. 앱을 실행하고 가로 방향으로 회전한다. 이제는 뷰가 올바르게 정렬 될 것이다. 다시 세로 방향으로 회전 시키고 모든 것이 잘 어울리는지 확인한다.

잘 동작한다. 그러나 사실 꽤나 간단한 레이아웃을 위해서 많은 양의 코드를 작성했다. 각각의 뷰가 크기 변경을 동적으로 하거나 서브뷰의 갯수가 정해지지 않는 것과 같은 정말 복잡한 레이아웃을 위해 걸리는 노력을 상상해 보아라.

분명히 다른 방법이 있을꺼야

메모: 다른 방법으로 접근하는 방법은 가로와 세로 방향의 nib을 구분해서 만드는 것이다. 디바이스가 회전 될 때 다른 nib의 뷰를 로드하고 기존 것과 교체한다. 그러나 이 방법도 여전히 많은 노력을 필요로 한다. 그리고 1개 대신에 2개의 nib을 관리하므로써 더 많은 문제점을 발생 시킬 수 있다.

오토 레이아웃을 구조하라!

이제 오토 레이아웃과 동일한 효과를 달성하는 방법을 볼 수 있다. 먼저 ViewController.m에서 willAnimateRotationToInterfaceOrientation:duration:을 지운다. 왜냐하면 코드를 작성하지 않고 이러한 작업을 할 것이다.

ViewController.xib을 선택하고 nib 파일의 오토 레이아웃을 활성화 하기 위해 File 인스펙터 패널에서 “Use Autolayout”의 체크박스를 체크한다.

오토 레이아웃 활성화

메모: 오토 레이아웃은 전체 nib 또는 스토리보드 파일을 위해서 항상 활성화 되어 있다. 체크박스가 체크되어 있으면 nib 또는 스토리보드안의 모든 뷰는 오토 레이아웃을 사용한다.

앱을 실행하고 가로 방향으로 회전 시켜라. 이전에 했던 작업과 같이 엉망으로 된 레이아웃이 보여진다.

잘못된 가로화면

오토 레이아웃을 시작하자. Cmd키를 누르고 위의 2개의 뷰(녹색과 노란색)를 클릭한다. 그러면 두 개는 선택된다. Xcode의 Editor 메뉴에서 PinWidths Equally를 선택한다.

Pin widths equally

똑같이 두 개의 뷰를 다시 선택하고 EditorPinHorizontal Spacing을 선택한다. (첫번째 Pin 작업을 수행한 후에 두 개의 뷰가 선택된 상태로 표시됨에도 불구하고, 특별한 레이아웃 관계 디스플레이 모드에 있는 것을 주의해라. 그래서 두 개의 뷰를 다시 선택 해야만 한다.)

왼쪽의Document Outline에서 “Constraints” 이름의 새로운 섹션을 발견했을 것이다. 이 섹션은 nib에 오토 레이아웃이 활성화되면 추가된다. 이 제약조건과 다음 섹션에서 그것들이 어떻게 동작하는지 배울 것이다.

지금은 “Horizontal Space (170)” 위치로 이동하고 리스트로부터 그것을 삭제 한다.

Horizontal Space Constraint

앱을 실행하고 가로 방향으로 회전 시킨다. 이전보다 보기 좋아 보인다. 위쪽 뷰는 적절한 너비와 패딩을 가진다. 그러나 아직 꽤 부족하다.

가로 방향은 거의 마쳤다

Cmd 키를 누르고 3개의 뷰 모두를 선택한다. Editor 메뉴에서 PinHeights Equally를 선택한다.

이제 왼쪽-위 코너의 뷰를 선택하고 아래쪽 뷰를 선택(방금 전처럼 Cmd를 사용하여)하고 EditorPinVertical Spacing를 선택한다.

마지막으로 “Vertical Space (240)” 제약조건을 리스트로부터 삭제한다.

동시에 3개의 뷰를 선택하면 인터페이스 빌더는 다음과 같이 무언가를 보여 줄 것이다.

제약조건

파란색 “T-bar” 모양은 뷰 사이의 제약조건을 나타낸다. 약간 무섭게 보일 수도 있으나 모든 의미를 터득하면 실제로는 매우 간단하다.

앱을 실행하고.. 짜잔~! 한 줄의 코드를 작성하지 않고도 모든 것이 제대로 보인다.

올바른 가로방향

정확하게 무슨 짓을 한 것일까? 얼마나 큰 뷰인지, 그것들이 어디에 위치해 있는지를 하드코딩을 요구하기 보다, 오토 레이아웃은 레이아웃의 뷰가 각각 어떻게 관련되어 있는지 표현 할 수 있다.

제약조건으로 알려진 다음 관계를 레이아웃에 삽입한다.

  • 왼쪽-위과 오른쪽-위 뷰는 항상 같은 넓이(Pin의 “Widths Equally” 명령어)를 가진다. .
  • 왼쪽-위과 오른쪽-위 뷰의 사이에 수평(Pin의 “Horizontal Spacing” 명령어)으로 20 포인트 패딩이 있다.
  • 모든 뷰는 항상 같은 높이(Pin의 “Heights Equally” 명령어)를 가진다.
  • 위쪽 뷰와 아래쪽 하나의 뷰 사이에는 수직(Pin의 “Vertical Spacing” 명령어)으로 20 포인트 패딩이 있다.

그리고 화면 크기가 변경되었을 때 그 방법은 뷰를 배치 해야하는 곳과 행동하는 방법인 오토 레이아웃으로 표현하기에 충분하다.

잘했어요

메모: 또한 “Use Autolayout” 체크박스를 토글 할 때 스프링-스트러츠 레이아웃으로부터 가져온 몇 가지 제약조건이 있다. 뷰와 화면의 가장자리 사이의 각각의 여백에 대해 “이 뷰는 항상 위/아래/왼쪽/오른쪽 가장자리로 부터 20포인트 거리에 위치한다.”와 같은 기본적으로 말하는 제약조건이 있다.

Document Outline에서 모든 제약조건을 볼 수 있다. Document Outline에 제약조건을 클릭을 하면, 인터페이스 빌더는 제약조건 주변에 흰색 아웃라인을 그리고 눈에 뛰기 위해 그것에 그림자를 추가해서 뷰를 강조 한다.

선택된 제약조건

제약조건은 실제 객체(NSLayoutConstraint 클래스)이다. 그리고 또한 속성을 가진다. 예를들면 위쪽 두개의 뷰 사이의 패딩을 추가하는 제약조건(“Horizontal Space (20)”라고 이름을 가진)을 선택하고 Attributes 인스팩터로 전환한다. Constant 필드를 편집하여 여백의 크기를 변경 할 수 있다.

Constraint 속성

100으로 설정하고 앱을 다시 실행한다. 이제 여백이 더 넓어졌다.

”가로화면에서

앱의 뷰를 설명 할 때 오토 레이아웃은 스프링과 스트러츠 보다 더 많은 표현이다. 이 튜토리얼의 끝에서 제약조건과 인터페이스 빌더에서 다른 종류의 레이아웃을 만들기 위해 그것들을 적용하는 방법에 대해 배울 것이다.

오토 레이아웃의 작동 방법

위의 테스트 드라이브에서 본 바와 같이, 오토 레이아웃의 기본 도구는 제약조건이다. 제약조건은 두개의 뷰 사이의 기하학적 관계를 설명한다. 예를 들어 다음과 같은 제약조건이 있을 수 있다.

”레이블의 오른쪽 가장자리는 버튼 B의 왼쪽 가장자리와 둘 사이의 빈 공간 20포인트로 연결되어 있다. ”

오토 레이아웃은 모든 제약조건을 가지고 모든 뷰의 이상적인 위치와 크기를 계산하기 위해 몇 가지 수학적 계산한다. 더 이상 뷰의 프레임을 설정 할 필요가 없다. 뷰에 설정한 제약조건을 전부 기초로 하여 오토 레이아웃은 그것을 대신 수행한다.

오토 레이아웃 이전에는 인터페이스 빌더로 특정 좌표에 배치를 하거나, 사각형을 initWithFrame:에 전달하거나, 뷰의 프레임, 범위(bound) 또는 중앙 속성을 설정하여 뷰의 프레임을 하드코딩 했었다.

간단하게 만들 앱을 위해서 구체적으로 다음 그림과 같이 프레임을 설정 한다.

Struts 좌표

각각의 뷰에 오토싸이징 마스크를 설정한다

Struts 오토싸이징 마스크

저 방법으로는 더 이상 화면 디자인을 생각하는 방법을 사용 할 수 없다. 오토 레이아웃으로 해야 할 모든 것은 다음과 같다.

스트러츠 대신에 오토 레이아웃

뷰의 크기와 위치는 더 이상 중요하지 않다. 오직 제약조건의 문제이다. 물론 새로운 버튼 또는 레이블을 캔버스에 드래그 할 때 특정 크기를 가진다. 그리고 특정 위치에 드롭을 할 것이다. 그러나 그것은 오직 제약조건을 넣기위해 인터페이스 빌더에 사용을 전달하는 디자인 도구이다.

의미하는 것 처럼 디자인 하기

제약조건을 사용하는 가장 큰 이점은 더 이상 적절한 위치에 뷰를 나타나게 하기 위해 좌표를 설정하지 않아도 된다. 대신에 오토 레이아웃에 어떻게 뷰가 다른 뷰와 연관되는지를 설명하면 된다. 그리고 오토 레이아웃은 모든 어려운 일을 대신 해 줄 것이다. 의도에 의한 디자인이라고 불린다.

의도에 의한 디자인을 할 때, 수행하고 싶은 것이 무엇인지 그러나 필요하지는 않지만 그것을 수행하는 방법을 표현한다. “버튼의 왼쪽-위 코너의 좌표는 (20, 230)” 이라고 하는 대신에, 이제는 다음과 같이 할 수 있다.

“버튼은 그것의 슈퍼뷰에 수직의 가운데에 있다. 그리고 그것은 슈퍼뷰의 왼쪽 가장자리로부터 고정된 거리에 위치한다.”

이 설명을 사용하여 오토 레이아웃은 자동적으로 버튼이 나타나는 위치를 계산한다. 슈퍼뷰가 크거나 작은 것은 문제가 아니다.

의도된 디자인 다른 예와 오토 레이아웃은 다음 지침을 모두 처리 할 수 있다.

“이 두 개의 텍스트 필드는 항상 같이 크기여야 한다.”
“이 두 개의 버튼은 항상 같이 움직여야 한다.”
“이 네 개의 레이블은 항상 오른쪽 정렬이여야 한다.”

이것은 유저 인터페이스 디자인을 더 설명적으로 만든다. 제약조건과 시스템이 자동적으로 프레임 계산하는 것을 단순하게 정의한다.

첫 번째 셋션에서 단지 몇 개의 뷰를 가지는 레이아웃이 가로/세로 두 방향을 제대로 레이아웃을 보여주기 위해서 꽤 많은 작업이 필요하다는 것을 보았다. 오토 레이아웃을 사용하면 저러한 모든 노력을 건너 뛸 수 있다. 제약조건을 제대로 설정한다면 레이아웃은 가로/세로 두 방향에서 어떠한 변경 없이 작동해야 한다.

오토 레이아웃을 사용하는 다른 중요한 이점은 지역화이다. 예를 들면 독일어로 된 텍스트는 매우 길기로 악명 높다. 레이블에 딱 맞게 하려면 머리가 아프다. 화면에 나타나는 것을 필요로 하는 콘텐츠를 기초로 해서 레이블의 크기를 자동으로 조절 가능하기 때문에 오토 레이아웃은 모든 이러한 작업을 대신한다. 그리고 나머지는 제약조건과 관련된다.

독일어, 프랑스어, 또는 다른 언어 지원을 추가하는 것은 제약조건, 텍스트 번역의 설정의 문제이다. 그게 전부이다.

프랑스어

오토 레이아웃의 익히는 가장 좋은 방법은 그것을 다루는 것이다. 그래서 이 튜토리얼의 남은 부분에서 할 일이다.

메모: 오토 레이아웃은 화면회전을 위해서만 유용한게 아니다. 이것은 크기가 다른 화면을 적용하기 위해서 확대 및 축소를 쉽게 할 수 있다. 큰 화면의 아이폰과 작아진 아이패드의 계속되는 루머가 사실로 되었을 때 이 방법은 유용하다. ☺

Courting 제약조건

현재 프로젝트를 닫고 Single View Application 템플릿을 사용하여 새로운 프로젝트를 생성한다. 이름은 “Constraints” 라고 한다. 스토리보드를 사용하지 않는 아이폰 프로젝트가 될 것이다. 그러나 ARC는 사용한다.

Xcode 4.5로 새로운 프로젝트를 생성하면 오토 레이아웃을 사용한다고 가정한다. 그래서 그것을 활성화 하기 위해 어떠한 작업도 할 필요가 없다.

인터페이스 빌더를 열고 ViewController.xib를 클릭한다. 새로운 Round Rect Button을 캔버스위에 드래그 한다. 드래그하는 동안 파란색 점선의 선이 나타난다. 이런 선은 가이드라고 알려져 있다.

가이드

화면의 여백의 주위에 가이드가 있을 뿐만 아니라 가운데도 있다.

다른 가이드

이전에 인터페이스 빌더를 사용했다면, 이런 가이드를 봤을 것이다. 그것은 오브젝트를 쉽게 정렬할 수 있도록 도움이 되는 힌트이다. 그러나 오토 레이아웃이 활성화되면 가이드는 다른 목적을 가진다. 아직 그것들을 정렬을 위해 사용 하지만 새로운 제약 조건이 어디에 갈 것인지 알려준다.

파란색 가이드에 대해 왼쪽-위 코너에 버튼을 드롭한다. 이제 nib은 다음과 같을 것이다.

가이드와 버튼

두 개의 파란색 선이 버튼에 붙어 있다. 이런 T자 모양의 객체는 이 버튼에 설정된 제약 조건이다.

모든 제약조건은 인터페이스 빌더 창의 왼쪽의 Document Outline 창에 리스트 되어 있다.

Document Outline의 버튼 제약조건

현재는 두 개의 제약조건이 있다. 버튼과 메인 뷰의 왼쪽 가장자리의 사이에 Horizontal Space과 버튼과 메인 뷰의 윗쪽 가장자리 사이의 Vertical Space이다. 이러한 제약조건에 의한 표현되는 관계는 다음과 같다.

“버튼은 항상 그것의 슈퍼뷰의 왼쪽-위 모서리에 위치 한다.”

이제 버튼을 선택하고 다시 파란색 가이드를 고려하여 오른쪽-위 코너에 그것을 위치 시킨다.

위-오른쪽 코너 버튼

Horizontal Space 제약조건은 변경되었다. 더 이상 버튼의 왼쪽에 붙어 있지 않고 오른쪽에 있다.

가이드에 대한 버튼(또는 어떠한 다른 뷰)을 배치 할 때, 애플의 iOS 휴먼 인터페이스 가이드 라인 문서인 “HIG”에 정의된 표준크기의 제약조건을 얻을 수 있다. 가장자리의 주변 여백의 표준크기는 20 포인트 여백이다.

비록 가이드가 없는 곳에 버튼을 배치 할 때, 그 자리에 제약 조건을 유지 하기 위해 Horizontal 또는 Vertical Space 을 여전히 얻을 수 있다. 사용해 봐라. 다음과 같이 무언가를 얻을 때까지 왼쪽으로 조금씩 버튼을 드래그 해라.

Button larger H space

Horizontal Space 제약조건이 여전이 있지만 지금은 그것이 커졌다. Document Outline에서 더 이상 표준 공간을 가지지 않은 것을 볼 수 있다.

Larger H space document outline

버튼의 위치에 따라 얻을 수 있는 제약 조건이 달라진다.

또한 “center” 제약 조건이 있다. 버튼을 캔버스의 가운데 밑에 드래그 한다. 그러면 가이드와 함께 자석 효과로 찰싹 붙는다.

가운데 밑에 위치한 버튼

Horizontal Space 제약조건은 버튼이 항상 그것의 슈퍼뷰의 중앙 정렬을 유지하는 가로축에서 Center X 정렬 제약조건으로 대체 됨을 의미한다. 아직 뷰의 하단으로부터 버튼을 유지 하기 위해 Vertical Space 제약조건이 있다. 다시말하면 표준 여백을 사용한다.

앱을 실행하고 가로방향으로 회전시킨다. 가로방향 모드에서도 버튼은 화면의 가운데 밑에 위치한다.

가로방향에서 가운데 밑에 위치한 버튼

의도를 표현하는 방법은 다음과 같다. “이 버튼은 항상 아래쪽 중앙에 위치해야 한다”. 인터페이스 빌더에 버튼의 정확한 위치를 전달 했던 곳이 없다. 오직 뷰에 위치할 곳만 전달 했었다.

오토 레이아웃으로는 캔버스 위에 뷰를 위치 시킬 정확한 좌표 또는 크기에 대해 더 이상 신경 쓸 필요가 없다. 대신에 오토 레이아웃은 설정한 또는 인터페이스 빌더가 대신 설정한 제약조건에서 이 두 가지를 이끌어 낸다.

버튼의 Size 인스팩터에서 지금은 꽤 다른 이러한 파라다임 변화를 볼 수 있다.

Different size inspectors

오토 레이아웃을 비활성화하면 X값, Y값, 넓이 또는 높이 필드를 입력해서 선택된 뷰의 위치나 크기를 변경한다. 오토 레이아웃을 활성화하면 새로운 값을 필드에 입력 할 수 있지만 항상 원하는 효과를 얻을 수 없다. 뷰가 이동한다면 인터페이스 빌더는 새로운 값으로 새로운 제약조건을 계산한다.

예를들어 넓이 값이 100으로 변경되면 캔버스는 다음과 같이 변화 한다.

고정된 넓이의 버튼

“Center X Alignment” 제약조건이 사라지고 그 위치에는 화면의 왼쪽 가장자리에 버튼으로 붙은 “Horizontal Space”가 있다. 뿐만 아니라 그 자신의 버튼에 100 포인트의 넓이(버튼 밑의 파랑새 바)를 강제적으로 가지게 하는 새로운 제약 조건이 있다.

또한 이 새로운 “Width” 제약조건을 왼쪽의 Document Outline에서 볼 수 있다.

Document outline에서 Width 제약조건

버튼과 그것의 슈퍼뷰 사이의 다른 제약조건과 다르게 Width 제약조건은 버튼 자신에 적용된다. 버튼과 버튼 사이의 제약조건으로 생각 할 수 있다.

다시 버튼을 드래그하면 Center X Alignment 제약조건과 함께 자석효과로 달라붙는다.

팁: 싸이즈 인스펙터의 위치와 크기가 변경이 제약조건을 망칠 수 있기 때문에, 이것을 하지 않도록 권한다. 대신, 레이아웃을 변경을 원하면 제약 조건을 변경한다.

왜 전에 버튼이 Width 제약조건을 가지지 않았는지 궁금할 수 있다. 오토 레이아웃은 제약조건이 없으면서 버튼의 넓이를 어떻게 알았을까?

버튼은 스스로 어느정도 넓이를 가져야 하는지 안다. 버튼 제목의 텍스트에 코너의 주변의 패딩을 더하는 것을 바탕으로 계산을 한다. 버튼에 배경 이미지를 설정하면, 또한 계정에 그것을 적용한다.

고유 콘텐츠 크기로 알려져 있다. 모든 콘트롤이 이것을 가지지 않지만 많은 것이 가지고 있다. (다른 예는 UILabel이다) 뷰가 자신의 원하는 크기를 계산 할 수 있다면, 그것의 특정 Width 또는 Height 제약조건 설정할 필요가 없다. 나중에 이와 같은 것을 더 볼 수 있다.

나는 뚱뚱하지 않아

최적의 크기로 버튼이 반환될려면 그것을 선택하고 Editor메뉴에서 Size to Fit Content을 선택한다. 이것은 명시적 Width 제약조건을 제거하고 버튼의 고유 콘텐츠 크기를 복원한다.

손뼉도 마추쳐야 소리가 난다

오직 뷰와 그것의 슈퍼뷰 사이에서는 가이드는 나타나지 않는다. 그러나 또한 같은 레벨의 뷰 계층의 뷰 사이에서도 나타나지 않는다. 이것을 설명하기 위해서 캔버스에 새로운 둥근 사각 버튼을 드래그 한다.

버튼을 서로 충분히 떨어지게 놓으면 새로운 버튼은 자신의 제약조건을 가진다. 그러나 각 버튼을 너무 가까이 놓으면 제약조건은 상호작용하기 시작한다.

새로운 버튼을 기존에 있던 버튼 옆에 놓는다. 그러면 자석효과로 옆에 붙는다.

Snap two buttons

꽤 많은 점선 가이드라인이 여기에 있다. 그러나 인터페이스 빌더는 모든 제약조건으로 바꿀 수 없다. 기본적으로 두 버튼은 다른 방법(위쪽, 가운데 그리고 베이스라인)으로 정렬 할 수 있게 인식한다.

버튼을 드롭하고 나서 제약조건은 다음과 같다.

Two buttons

새로운 버튼은 화면의 아래에 Vertical Space를 가진다. 또한 다른 버튼과 함께 연결하는 Horizontal Space 가진다. 이 공간은 고작 8포인트 밖에 안되게 작기 때문에 T자 바를 찾기 힘들다. 그러나 거기에 있다.

Document Outline에서 Horizontal Space 제약조건을 클릭한다

Highlighted H-space between buttons

제약조건을 선택할 때 속해 있는 것은 노란색 불빛이 나게 된다. 이 특별한 제약조건은 두 개의 버튼에서 일어난다. 여기에 한 것은 이렇게 말할 수 있다.

“두 번째 버튼은 항상 첫 번째의 오른쪽에 나타나며 첫 번째 버튼이 어디에 있는지 또는 얼만큼 큰지는 문제가 안된다.”

왼쪽 버튼을 선택하고 그것의 레이블은 “A longer label”과 같이 길게 작성한다. 그것을 마치면 버튼의 크기는 새로운 텍스트에 맞춰 재조절 된다. 그리고 다른 버튼은 비켜준다. 결국 첫 번째 버튼의 오른쪽 가장자리에 붙는다. 이것이 정확하게 일어나길 바란 일이다.

긴 레이블을 가진 버튼

인터페이스 빌더가 다시 Horizontal Space의 첫 번째 버튼의 Center X Alignment를 대체 한 것을 확인할 수 있다. 매번 콘트롤의 크기나 위치를 변경 할 때, 인터페이스 빌더는 최적의 제약조건의 설정을 다시 계산한다. 대부분 올바르게 동작하지만 때때로 잘못되게 동작한다. 명백하게 버튼의 텍스트를 단지 변경하고 싶지만 여기서는 중심을 유지한다.

버튼을 다시 중심으로 드래그 한다. 이제 제약조건은 다음과 같이 된다.

연결이 끊긴 두 버튼

저것은 아마도 원하는 일이 아니다. 두 개의 버튼은 더 이상 서로에 연결되어 있지 않는다. 대신 가장 오른쪽 버튼은 이제 화면의 오른쪽 가장자리에 Horizontal Space를 가진다. 두 개의 버튼 사이의 Horizontal Space 제약조건은 더 이상 없다.

물론, 다시 자석효과로 붙여서 두 개의 버튼을 재연결 할 수 있다. 그러나 이 문제는 뷰의 주변을 드래그 하지 않음으로 피할 수 있다.

첫 번째로 Cmd-Z를 눌러 undo를 한다. 그래서 첫 번째 버튼은 더 이상 중앙 정렬이 아니다. 버튼을 선택하고 Editor메뉴에서 Container의 AlignHorizontal Center을 선택한다. 이번에는 첫 번째 버튼을 중앙으로 옮길 뿐만 아니라 다른 버튼도 함께 이동한다. 그것이 더 좋다.

이 작업의 방법에 더 좋은 느낌을 얻기 위해 여기 더 다른 작업을 한다. 작업 버튼을 선택하고 다른 하나는 위로 이동 시킨다. 그러면 수직으로 자석효과로 달라 붙는다. (그러나 두 개 버튼의 왼쪽 가장자리로 정렬을 하지 마라.)

Button on top

두 버튼을 서로 자석효과로 붙여 놓았기 때문에, 이제 두 개의 버튼 사이에는 Vertical Space가 있다. 다시 HIG에서 추천하는 8 포인트의 표준 공간을 가진다.

한글번역파트는 글자수 초과문제로 각파트를 2개의 파트로 나눈다. 계속해서 이 튜토리얼의 파트 1-2를 읽어라.

반응형
Posted by 컴스터
,

압축풀기 샘플

IPHONE 2014. 1. 23. 10:37
반응형

https://github.com/samsoffes/ssziparchive

반응형
Posted by 컴스터
,
반응형

// StoreKit.framework 라이브러리 추가 하기.


// StoreKitDemoViewController.h


#import <UIKit/UIKit.h>
#import <StoreKit/StoreKit.h>

@interface StoreKitDemoViewController : UIViewController <SKStoreProductViewControllerDelegate>

- (IBAction)showStoreView:(id)sender;

@end


// StoreKitDemoViewController.m


#import "StoreKitDemoViewController.h"

@interface StoreKitDemoViewController ()

@end

@implementation StoreKitDemoViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)showStoreView:(id)sender {
   
    SKStoreProductViewController *storeViewController = [[SKStoreProductViewController alloc] init];
    storeViewController.delegate = self;
   
    NSDictionary *parameters = @{SKStoreProductParameterITunesItemIdentifier:[NSNumber numberWithInteger:776483996]}; // 아이디 번호. 앱스토어에서 마우스 오른쪽 클릭시 링크복사후 맨뒤에 아이디 번호임.
   
    [storeViewController loadProductWithParameters:parameters completionBlock:^(BOOL result, NSError *error) {
        if (result)
        {
            [self presentViewController:storeViewController animated:YES completion:nil];
        }
    }];
}

#pragma mark -
#pragma mark SKStoreProductViewControllerDelegate

- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController
{
    [viewController dismissViewControllerAnimated:YES completion:nil];
}

@end

반응형
Posted by 컴스터
,
반응형

// 1. 파란색 투명한 공 모양으로 지도에 현재 위치를 나타낸다.

_mapView.showsUserLocation = YES;


// 2. 현재의 위치를 지도의 가운데로 위치시키고 영역 폭을 50미터로 변경.

MKUserLocation *userLocation = _mapView.userLocation;
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.location.coordinate, 50, 50);


[_mapView setRegion:region animated:NO];


// 3. 사용자 이동에 따른 MapView 업데이트 하기.

_mapView.delegate = self;


- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
    _mapView.centerCoordinate = userLocation.location.coordinate;
}


// 4. 맵 뷰에 어노테이션 추가하기.(예. 마이크로소프트의 본사가 위치한 워싱텅주의 레드몬드)

CLLocationCoordinate2D locationCoordinate2D;
   
locationCoordinate2D.latitude = 47.640071;
locationCoordinate2D.longitude = -122.129598;
   
 MKPointAnnotation *pointAnnotation = [[MKPointAnnotation alloc] init];
 pointAnnotation.coordinate = locationCoordinate2D;
 pointAnnotation.title = @"Microsoft";
 pointAnnotation.subtitle = @"Microsoft's headquarters";
 [_mapView addAnnotation:pointAnnotation];

반응형
Posted by 컴스터
,

로컬 노티피케이션

IPHONE 2014. 1. 16. 17:32
반응형

1. 로컬 노티피케이션 예약하기.

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // 백그라운드로 간후 10초후 발생.
    NSDate *alertTime = [[NSDate date] dateByAddingTimeInterval:10];
    UIApplication *app = [UIApplication sharedApplication];
    UILocalNotification *notifyAlarm = [[UILocalNotification alloc] init];
   
    if (notifyAlarm)
    {
        notifyAlarm.fireDate = alertTime;
        notifyAlarm.timeZone = [NSTimeZone defaultTimeZone];
        notifyAlarm.repeatInterval = 0;
        notifyAlarm.soundName = @"bell_tree.mp3";
        notifyAlarm.alertBody = @"Staff meeting in 30 minutes";
        [app scheduleLocalNotification:notifyAlarm];
    }
}


2. 예약된 노티피케이션 취소하기.

UIApplication *app = [UIApplication sharedApplication];

NSArray *oldNotifications = [app scheduledLocalNotifications];


if([oldNotifications count] > 0)

[app cancelAllLocalNotifications];


3. 첫번째 로컬 노티피케이션 즉시 호출하기.

NSArray *notifications = [app scheduledLocalNotifications];

if([notifications count] > 0)

[app presentLocalNotificationNow:notifications[0]];

반응형
Posted by 컴스터
,


반응형