UITableView 유연 / 동적 heightForRowAtIndexPath
케이스
일반적으로 cellForRowAtIndexPath
델리게이트 방법을 사용 하여 셀을 설정합니다. 셀에 설정된 정보는 셀을 그리는 방법과 크기에 중요합니다.
불행히도 heightForRowAtIndexPath
델리게이트 메서드는 델리게이트 메서드보다 먼저 호출 cellForRowAtIndexPath
되므로 셀의 높이를 반환하도록 델리게이트에게 말할 수는 없습니다.이 값은 당시 0이되기 때문입니다.
따라서 셀이 표에 그려지기 전에 크기를 계산해야합니다. 운 좋게도 sizeWithFont
NSString 클래스에 속하는 이 작업을 수행하는 메서드가 있습니다 . 그러나 문제가 있습니다. 정확한 크기를 동적으로 계산하려면 셀의 요소가 어떻게 표시되는지 알아야합니다. 나는 이것을 예에서 분명히 할 것이다.
UITableViewCell
라는 레이블이 포함 된을 상상해보십시오 textLabel
. cellForRowAtIndexPath
델리게이트 메서드 내에 배치합니다 textLabel.numberOfLines = 0
. 기본적으로 레이블에 특정 너비에 대한 텍스트를 표시하는 데 필요한만큼의 줄을 가질 수 있음을 알려줍니다. textLabel에 원래 textLabel에 제공된 너비보다 큰 텍스트를 제공하면 문제가 발생합니다. 두 번째 줄이 나타나지만 셀의 높이가 자동으로 조정되지 않으므로 테이블보기가 엉망이됩니다.
앞에서 말했듯 sizeWithFont
이 높이를 계산하는 데 사용할 수 있지만 어떤 글꼴이 사용되는지, 너비 등을 알아야합니다. 간단하게 너비 만 신경 쓰는 경우 너비가 주변에 있도록 하드 코딩 할 수 있습니다. 320.0 (패딩을 고려하지 않음). 그러나 일반 대신 UITableViewStyleGrouped를 사용하면 너비가 약 300.0이되고 셀이 다시 엉망이됩니다. 또는 세로에서 가로로 바꾸면 더 많은 공간이 있지만 300.0을 하드 코딩했기 때문에 사용되지 않습니다.
이것은 어느 시점에서 하드 코딩을 얼마나 피할 수 있는지 스스로에게 질문해야하는 경우입니다.
내 생각
cellForRowAtIndexPath
UITableView 클래스에 속하는 메서드를 호출하여 특정 섹션 및 행에 대한 셀을 가져올 수 있습니다. 나는 당신이 그것을 원하지 않는다고 말한 몇 개의 게시물을 읽었지만 정말로 이해하지 못합니다. 예, 이미 셀을 할당한다는 데 동의하지만 heightForRowAtIndexPath
델리게이트 메서드는 표시 될 셀에 대해서만 호출되므로 셀이 할당됩니다. 올바르게 사용하면 메서드 dequeueReusableCellWithIdentifier
에서 셀이 다시 할당되지 cellForRowAtIndexPath
않고 대신 포인터가 사용되고 속성이 조정됩니다. 그럼 문제가 뭐죠?
셀은 cellForRowAtIndexPath
delegate 메서드 내에서 그려지지 않습니다 . 테이블 뷰 셀이 표시되면 스크립트는 setNeedDisplay
UITableVieCell 에서 메서드를 호출 drawRect
하여 셀을 그리는 메서드를 트리거합니다 . 따라서 cellForRowAtIndexPath
대리자를 직접 호출 해도 두 번 그려야하므로 성능이 저하되지 않습니다.
좋습니다. cellForRowAtIndexPath
대리자 메서드 내에서 heightForRowAtIndexPath
대리자 메서드 를 호출하여 셀 크기를 결정하는 데 필요한 모든 정보를받습니다.
sizeForCell
셀이 Value1 스타일 또는 Value2 등에있는 경우 모든 옵션을 통해 실행되는 자체 메서드를 만들 수 있습니다 .
결론 / 질문
제 생각에 설명했던 이론 일뿐입니다. 제가 쓴 내용이 정확한지 알고 싶습니다. 아니면 같은 일을 수행하는 다른 방법이있을 수도 있습니다. 가능한 한 유연하게 일을 할 수 있기를 원합니다.
예, 이미 셀을 할당한다는 데 동의하지만 heightForRowAtIndexPath 대리자 메서드는 표시 될 셀에 대해서만 호출되므로 셀이 할당됩니다.
이것은 올바르지 않습니다. 테이블보기는 heightForRowAtIndexPath
현재 표시되고있는 행뿐만 아니라 테이블보기에있는 모든 행에 대해 호출 (구현 된 경우)해야합니다. 그 이유는 올바른 스크롤 표시기를 표시하려면 전체 높이를 파악해야하기 때문입니다.
나는 이것을 다음과 같이 사용했습니다.
테이블 뷰에 사용될 컬렉션 개체를 기반으로 컬렉션 개체 (크기 정보 배열 (사전, NSNumber of row heights 등))를 생성합니다.
이것은 로컬 또는 원격 소스에서 데이터를 처리 할 때 수행됩니다.
이 컬렉션 개체를 만들 때 사용할 글꼴의 유형과 크기를 미리 결정합니다. UIFont 개체 또는 콘텐츠를 나타내는 데 사용되는 사용자 지정 개체를 저장할 수도 있습니다.
이 컬렉션 개체는 UITableViewCell 인스턴스 및 하위보기 등의 크기를 결정하기 위해 UITableViewDataSource 또는 UITableViewDelegate 프로토콜을 구현할 때마다 사용됩니다.
이렇게하면 콘텐츠의 다양한 크기 속성을 얻기 위해 UITableViewCell을 하위 클래스로 만들지 않아도됩니다.
프레임 초기화에 절대 값을 사용하지 마십시오. 현재 방향 및 경계를 기준으로 상대 값을 사용합니다.
임의의 방향으로 회전하면 런타임에 크기 조정 메커니즘을 수행하십시오. autoresizingMask가 올바르게 설정되었는지 확인하십시오.
높이 만 필요하며 행 높이를 결정하기 위해 UITableViewCell 내부에 불필요한 모든 것이 필요하지는 않습니다. 너비 값은 뷰 경계에 상대적이어야하므로 너비가 필요하지 않을 수도 있습니다.
이 문제를 해결하기위한 접근 방식은 다음과 같습니다.
- 이 솔루션에서는 하나의 레이블에만 "동적"높이가 있다고 가정합니다.
- 또한 레이블을 자동 크기로 만들어 셀이 커짐에 따라 높이를 늘리면 셀 높이 만 변경하면된다고 가정합니다.
- 펜촉이 레이블이있을 위치와 위 및 아래에있는 공간에 대한 적절한 간격이 있다고 가정합니다.
- 펜촉에서 레이블의 글꼴이나 위치를 변경할 때마다 코드를 변경하고 싶지 않습니다.
높이를 업데이트하는 방법 :
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// We want the UIFont to be the same as what is in the nib,
// but we dont want to call tableView dequeue a bunch because its slow.
// If we make the font static and only load it once we can reuse it every
// time we get into this method
static UIFont* dynamicTextFont;
static CGRect textFrame;
static CGFloat extraHeight;
if( !dynamicTextFont ) {
DetailCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
dynamicTextFont = cell.resizeLabel.font;
CGRect cellFrame = cell.frame;
textFrame = cell.resizeLabel.frame;
extraHeight = cellFrame.size.height-textFrame.size.height; // The space above and below the growing field
}
NSString* text = .... // Get this from the some object using indexPath
CGSize size = [text sizeWithFont:dynamicTextFont constrainedToSize:CGSizeMake(textFrame.size.width, 200000.f) lineBreakMode:UILineBreakModeWordWrap];
return size.height+extraHeight;
}
문제 :
- 프로토 타입 셀을 사용하지 않는 경우 셀이 nil인지 확인하고 초기화해야합니다.
- 펜촉 / 스토리 보드에는 UILabel 자동 크기가 있어야하며 여러 줄이 0으로 설정되어 있어야합니다.
TTTableItemCell.m
Three20 프레임 워크를 살펴보아야 합니다. 기본적으로 각 셀 클래스 (글꼴, 레이아웃 등과 같은 일부 미리 정의 된 설정 포함)가 공유 메서드 + tableView: sizeForItem:
(또는 이와 유사한 것 )를 구현하도록 하여 항목 개체의 텍스트를 전달 하는 다른 접근 방식을 따릅니다 . 특정 셀에 대한 텍스트를 찾을 때 적절한 글꼴도 찾을 수 있습니다.
Regarding the cell height: You can check your tableView's width and, if necessary, subtract the margins by UITableViewStyleGrouped
and the width an eventual index bar and disclosure item (which you look for in the data storage for your cells' data). When the width of the tableView changes, e.g. by interface rotation, you have to call [tableView reloadData]
.
To answer the question the original poster asked which was 'is it ok to call cellForRowAtIndexPath?', it's not. That will give you a cell but it will NOT allocate it to that indexPath internally nor will it be re-queued (no method to put it back), so you'll just lose it. I suppose it will be in an autorelease pool and will be deallocated eventually, but you'll still be creating loads of cells over and over again and that is really pretty wasteful.
You can do dynamic cell heights, you can even make them look quite nice, but it's a lot of work to really make them look seamless, even more if you want to support multiple orientations etc.
I have an idea about dynamic cell height.
Just create one instance of your custom cell as member variable of UITableViewController
. In the tableView:heightForRowAtIndexPath:
method set the cell's content and return the cell's height.
This way you won't be creating/autoreleasing cell multiple times as you will if you call cellForRowAtIndexPath
inside the heightForRowAtIndexPath
method.
UPD: For convenience, you can also create a static method in your custom cell class that will create a singleton cell instance for height calculation, set the cell's content and then return it's height.
tableView:heightForRowAtIndexPath:
function body will now look like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [MyCell cellHeightForContent:yourContent];
}
Here's my solution which I've used to implement some rather slick cells for a chatting app.
Up to this point I've always been really really irritated with heightForCellAtIndexPath: because it leads to violating the DRY principle. With this solution my heightForRowAtIndexPath: costs 1.5ms per cell which I could shave down to ~1ms.
Basically, you want each subview inside your cell to implement sizeThatFits: Create an offscreen cell which you configure then query the root view with sizeThatFits:CGSizeMake(tableViewWidth, CGFLOAT_MAX).
There are a few gotchas along the way. Some UIKit views have expensive setter operations. For example -[UITextView setText] does a lot of work. The trick here is to create a subclass, buffer the variable, then override setNeedsDisplay to call -[super setText:] when the view is about to be rendered. Of course, you'll have to implement your own sizeThatFits: using the UIKit extensions.
참고URL : https://stackoverflow.com/questions/2213024/uitableview-flexible-dynamic-heightforrowatindexpath
'Development Tip' 카테고리의 다른 글
node_modules 위치 변경 (0) | 2020.12.05 |
---|---|
Android 메모리 가이드의 '종속성 주입 프레임 워크 방지'가 Dagger에도 적용 되나요? (0) | 2020.12.05 |
바이너리 파일을 부호없는 문자 벡터로 읽는 방법 (0) | 2020.12.05 |
Haskell에 ML 스타일 모듈을 추가하는 데있어 가장 큰 이론적 어려움은 무엇입니까? (0) | 2020.12.05 |
RequireJS로 지연 로딩을 달성하는 방법은 무엇입니까? (0) | 2020.12.05 |