[iOS Study] UITableViewCell 하위 클래스 만들기 |
출처 : 아론 힐리가스의 iOS 프로그래밍
-
UITableView 는 UITableViewCell 객체의 목록을 표시한다.
대다수 앱들은 기본 셀과 textLabel, detailTextLabel, imageView 로도 충분하다.
하지만 좀 더 자세하거나 다른 레이아웃의 셀이 필요할 땐 UITableViewCell 의 하위 클래스를 만들어야 한다.
-
UIView 의 하위 클래스를 만들 때, 종종 뷰의 모양을 커스터마이즈하기 위해 drawRect: 메소드를 재정의한다.
하지만 UITableViewCell 의 하위 클래스를 만들 떄는 대개 하위뷰들을 셀에 추가하여 모양을 커스터마이즈한다.
하위뷰들을 셀에 직접 추가하지 않고 셀의 콘텐트 뷰(content view)에 추가한다.
-
하위뷰들을 셀 자체에 직접 추가하지 않고 contentView 에 추가하는 것은 셀이 특정한 시기에 contentView 의 크기를 조절하기 때문에 중요하다.
예를 들어, 테이블뷰가 편집 모드에 들어가면 contentView 는 편집 컨트롤을 위한 공간을 만들기 위해 그 자체의 크기를 조절한다.
만약 하위뷰들을 UITableViewCell 에 직접 추가하면 편집 컨트롤들이 하위뷰들을 가릴 것이다.
셀은 편집 모드일 때 자신의 크기를 조절할 수 없지만 ( 테이블 뷰의 너비는 반드시 그대로 둔다. ) contentView 는 크기를 조절할 수 있다.
-
셀 계층구조에서 UIScrollView 를 주목해 봐야 한다.
이것이 바로 편집 모드에 들어갔을 떄 iOS 가 셀의 내용을 왼쪽으로 움직이는 방법이다.
셀에서 오른쪽에서 왼쪽으로 스와이프하여 삭제 컨트롤을 나타낼 수 있고
이 작업을 수행하기 위해 같은 스크롤뷰를 사용한다.
-
UITableViewCell 의 하위 클래스를 구성하는 가장 쉬운 방법은 XIB 파일을 사용하는 것이다.
-
Custom table cell 을 적용하기 위해서는 아래와 같은 코드가 필요하다.
- (void)viewDidLoad{
[super viewDidLoad];
UINib *nib = [UINib nibWithNibName:@“BNRItemCell” bundle:nil];
[self.tableView registerNib:nib forCellReuseIdentifier:@“BNRItemCell”];
}
사용할 때는
BNRItemCell* cell = [tableView dequeueReusableCellWithIdentifier:@"BNRItemCell"];
-
테이블뷰의 너비가 바뀔 때 각 셀들의 너비 또한 테이블뷰와 같은 크기로 변한다.
따라서 너비의 변화를 처리할 셀에 제약조건을 설정해야 한다.
(셀의 높이는 명시적으로 테이블뷰에 rowHeight 프로퍼티나 tableView:heightForRowAtIndexPath: 델리게이트 메소드를 통해 높이를 바꾸도록 요청하지 않는 한 바뀌지 않는다.)
-
Image 의 thumbnail 을 만드는 코드는 아래의 형태.
- (void)setThumbnailFromImage:(UIImage *)image{
CGSize origImageSize = image.size;
CGRect newRect = CGRectMake(0,0,40,40);
float ratio = MAX(newRect.size.width / origImageSize.width, newRect.size.height/origImageSize.height);
UIGraphicsBeginImageContextWithOptions(newRect.size, NO, 0.0);
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:newRect cornerRadius:5.0];
[path addClip];
CGRect projectRect;
projectRect.size.width = ratio * origImageSize.width;
projectRect.size.height = ratio * origImageSize.height;
projectRect.origin.x = (newRect.size.width - projectRect.size.width) / 2.0;
projectRect.origin.y = (newRect.size.height - projectRect.size.height) / 2.0;
[image drawInRect:projectRect];
UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
self.thumbnail = smallImage;
UIGraphicsEndImageContext();
}
-
오프스크린 이미지 컨텍스트를 만들려면 UIGraphicsBeginImageContextWithOptions 함수를 사용한다.
이 함수는 이미지 컨텍스트의 높이와 너비를 가진 CGSize 구조체와 환산 계수, 이미지의 불투명 여부를 받는다.
이 함수가 호출되면 새로운 CGContextRef 가 만들어져 현재 컨텍스트가 된다.
CGContextRef 에 그리기 위해서는코어 그래픽스를 사용한다.
그린 후에 이 컨텍스트에서 UIImage 를 얻으려면 UIGraphicsGetImageFromCurrentImageContext 함수를 호출한다.
이미지 컨텍스트로부터 이미지를 생성한 다음에는 UIGraphicsEndImageContext 함수를 사용하여 해당 컨텍스트를 반드시 정리해주어야 한다.
-
블록은 힙에서 생성되는 다른 객체들과 대조적으로 스택에서 생성된다.
이는 블록을 선언한 메소드가 완료할 때, 생성된 모든 블록이 다른 지역변수와 함께 소멸된다는 것을 의미한다.
블록이 선언된 메소드의 종료 이후에도 해당 블록을 유지하기 위해서는 블록에 copy 메시지를 보내야 한다.
그렇게 해서 블록은 힙에 복사된다.
@property (nonatomic, copy) void (^blockName)(void);
-
블록 정의는 void (^)(NSString *someArg, int another Arg) 의 형태로 정의된다.
-
다음과 같은 코드로 popover 가 뜨는 위치와 사이즈를 조정할 수 있다.
CGRect rect = [self.view convertRect:cell.thumbnailView.bounds fromView:cell.thumbnailView];
imagePopover.popoverContentSize = CGSizeMake(600, 600)
[imagePopover presentPopoverFromRect:rect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
cf) [A convertRect:CRect toView:B] // A 기준의 C Rect를 B 기준으로 변환
[A convertRect:CRect fromView:B] // B 기준의 C Rect를 A 기준으로 변환
-
블록은 인클로징 범위(enclosing scope) 안에 보이는 변수를 사용할 수 있다.
블록의 인클로징 범위는 그 블록이 정의된 메소드 내의 영역이다.
따라서 블록은 이 메소드의 모든 지역 변수, 메소드에 전달된 인자, 메소드를 실행한 객체의 인스턴스 변수에 접근할 수 있다.
-
capturing ‘cell’ strongly in this block is likely to lead to a retain cycle 을 끊기 위해서는 weak reference 를 지정해야 한다.
__weak BNRItemCell *weakCell = cell;
^block{
BNRItemCell *strongCell = weakCell;
...
}
블록이 시작되고 나면 실행이 끝날 때까지 셀을 유지해야 한다.
그런 이유로 strongCell 로 셀에 대한 강한 참조를 만들어 그 셀의 강한 소유권을 일시적으로 얻는다.
이 방법이 인클로징 범위에서 변수의 영구적인 강한 참조를 얻는 것과 달리 해당 블록은 strongCell 변수가 존재하는 경우에만 셀 객체의 강한 참조를 가진다.
즉, 블록이 실제 실행 중인 경우에만 강한 참조를 가진다.
-
UICollectionView 는 UIScrollView 의 하위 클래스이다.
UITableViewCell 대신에 UICollectionViewCell 로부터 상속된 셀을 표시한다.
이 타입의 셀을 제공하는 데이터 소스를 갖는다.
셀이 선택됐는지와 같은 정보를 얻을 수 있는 델리게이트를 갖는다.
UITableViewController 와 비슷하게, UICollectionViewController 도 UICollectionView 를 만들어 그 뷰의 델리게이트와 데이터 소스가 되는 뷰 컨트롤러 클래스이다.
-
테이블뷰는 셀의 한 컬럼만을 표시하지만, UICollectionView 는 GridView 와 같은 형태가 가능하다.
CollectionView 는 각 셀의 속성(위치, 크기 등)을 제어하는 레이아웃 객체를 가진다.
이 레이아웃 객체는 추상 클래스 UICollectionViewLayout 으로부터 상속받은 것이다.
격자 안에 셀을 놓는다면 UICollectionViewFlowLayout 인스턴스를 사용할 수 있다.
다른 형태를 원한다면 UICollectionViewLayout 의 커스텀 하위 클래스를 만들어야 한다.
-
기본 UITableViewCell 은 꽤 유용한다.
그러나 UICollectionViewCell 은 그렇지 않다.
콘텐트뷰를 가지지만 콘텐트뷰의 하위뷰는 없다.
따라서 UICollectionView 를 만든다면 UICollectionViewCell 의 하위 클래스를 만들 필요가 있다.
'프로그래밍 놀이터 > iOS' 카테고리의 다른 글
[iOS Study] 웹 서비스와 UIWebView (0) | 2016.03.15 |
---|---|
[iOS Study] 유동글자 (0) | 2016.03.09 |
[iOS Study] 저장, 읽기, 앱 상태 (0) | 2016.03.07 |
[iOS Study] 애니메이션 제어 (0) | 2016.03.06 |
[iOS Study] 자동 회전, 팝오버 컨트롤러, 모달 뷰 컨트롤러 (0) | 2016.03.05 |
댓글