요청하는 것은 UICollectionView를 사용하여 UITableView와 같은 레이아웃을 생성하는 방법 인 것처럼 들립니다. 이것이 실제로 원하는 경우,이를 수행하는 올바른 방법은 사용자 정의 UICollectionViewLayout 하위 클래스 ( SBTableLayout 과 유사 할 수 있음 )를 사용하는 것입니다.
반면에 기본 UICollectionViewFlowLayout을 사용하여이를 수행 할 수있는 깨끗한 방법이 있는지 정말로 묻는다면 방법이 없다고 생각합니다. iOS8의 자체 크기 조정 셀을 사용하더라도 간단하지 않습니다. 근본적인 문제는 흐름 레이아웃의 기계가 한 차원을 수정하고 다른 차원이 응답하도록 할 방법을 제공하지 않는다는 것입니다. (또한 가능하더라도 여러 줄 레이블의 크기를 조정하는 데 두 개의 레이아웃 패스가 필요하다는 점에서 추가적인 복잡성이있을 수 있습니다. 이것은 자체 크기 조정 셀이 systemLayoutSizeFittingSize에 대한 한 번의 호출을 통해 모든 크기 조정을 계산하려는 방식과 맞지 않을 수 있습니다.)
그러나 흐름 레이아웃을 사용하여 자체 크기를 결정하고 컬렉션보기의 너비에 자연스럽게 반응하는 셀을 사용하여 테이블보기와 같은 레이아웃을 만들고 싶다면 물론 가능합니다. 여전히 지저분한 방법이 있습니다. "셀 크기 조정", 즉 컨트롤러가 셀 크기를 계산할 때만 유지하는 표시되지 않는 UICollectionViewCell로 수행했습니다.
이 접근 방식에는 두 부분이 있습니다. 첫 번째 부분은 컬렉션 뷰 대리자가 컬렉션 뷰의 너비를 취하고 크기 조정 셀을 사용하여 셀의 높이를 계산하여 올바른 셀 크기를 계산하는 것입니다.
UICollectionViewDelegateFlowLayout에서 다음과 같은 메소드를 구현합니다.
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
// NOTE: here is where we say we want cells to use the width of the collection view
let requiredWidth = collectionView.bounds.size.width
// NOTE: here is where we ask our sizing cell to compute what height it needs
let targetSize = CGSize(width: requiredWidth, height: 0)
/// NOTE: populate the sizing cell's contents so it can compute accurately
self.sizingCell.label.text = items[indexPath.row]
let adequateSize = self.sizingCell.preferredLayoutSizeFittingSize(targetSize)
return adequateSize
}
이렇게하면 컬렉션 뷰가 둘러싸는 컬렉션 뷰를 기반으로 셀의 너비를 설정하지만 크기 조정 셀에 높이를 계산하도록 요청합니다.
두 번째 부분은 높이를 계산하기 위해 자체 AL 제약 조건을 사용하도록 크기 조정 셀을 가져 오는 것입니다. 이것은 다중 라인 UILabel이 효과적으로 2 단계 레이아웃 프로세스를 요구하는 방식으로 인해 예상보다 어려울 수 있습니다. 작업은 preferredLayoutSizeFittingSize
다음과 같이 메서드에서 수행됩니다 .
/*
Computes the size the cell will need to be to fit within targetSize.
targetSize should be used to pass in a width.
the returned size will have the same width, and the height which is
calculated by Auto Layout so that the contents of the cell (i.e., text in the label)
can fit within that width.
*/
func preferredLayoutSizeFittingSize(targetSize:CGSize) -> CGSize {
// save original frame and preferredMaxLayoutWidth
let originalFrame = self.frame
let originalPreferredMaxLayoutWidth = self.label.preferredMaxLayoutWidth
// assert: targetSize.width has the required width of the cell
// step1: set the cell.frame to use that width
var frame = self.frame
frame.size = targetSize
self.frame = frame
// step2: layout the cell
self.setNeedsLayout()
self.layoutIfNeeded()
self.label.preferredMaxLayoutWidth = self.label.bounds.size.width
// assert: the label's bounds and preferredMaxLayoutWidth are set to the width required by the cell's width
// step3: compute how tall the cell needs to be
// this causes the cell to compute the height it needs, which it does by asking the
// label what height it needs to wrap within its current bounds (which we just set).
let computedSize = self.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
// assert: computedSize has the needed height for the cell
// Apple: "Only consider the height for cells, because the contentView isn't anchored correctly sometimes."
let newSize = CGSize(width:targetSize.width,height:computedSize.height)
// restore old frame and preferredMaxLayoutWidth
self.frame = originalFrame
self.label.preferredMaxLayoutWidth = originalPreferredMaxLayoutWidth
return newSize
}
(이 코드는 "고급 컬렉션보기"에있는 WWDC2014 세션의 샘플 코드에있는 Apple 샘플 코드에서 수정되었습니다.)
주목해야 할 몇 가지 사항이 있습니다. 레이블의 너비를 계산하고 설정하기 위해 layoutIfNeeded ()를 사용하여 전체 셀의 레이아웃을 강제 적용합니다. 하지만 그것만으로는 충분하지 않습니다. preferredMaxLayoutWidth
레이블이 자동 레이아웃에서 해당 너비를 사용 하도록 설정해야한다고 생각합니다 . 그리고 systemLayoutSizeFittingSize
나서야 레이블을 고려하면서 셀이 높이를 계산하도록 할 수 있습니다.
이 접근 방식이 마음에 드나요? 아니!! 너무 복잡하고 레이아웃을 두 번 수행합니다. 그러나 성능이 문제가되지 않는 한, 코드에서 레이아웃을 두 번 정의하는 것보다 런타임에 레이아웃을 두 번 수행하는 것이 유일한 대안 인 것 같습니다.
내 희망은 결국 자체 크기 조정 세포가 다르게 작동하고이 모든 것이 훨씬 더 간단해질 것이라는 것입니다.
직장에서 그것을 보여주는 예제 프로젝트 .
그러나 왜 자체 크기 조정 셀을 사용하지 않습니까?
이론적으로 iOS8의 새로운 "셀 자체 크기 조정"기능은이를 불필요하게 만들어야합니다. 자동 레이아웃 (AL)을 사용하여 셀을 정의한 경우 컬렉션보기는 자체 크기를 조정하고 올바르게 배치 할 수있을만큼 스마트해야합니다. 실제로 여러 줄 레이블과 함께 작동하도록 만든 예제는 본 적이 없습니다. 나는 이것이 부분적으로 자체 크기 조정 세포 메커니즘이 여전히 버그가 있기 때문 이라고 생각 합니다 .
하지만 대부분은 자동 레이아웃 및 라벨의 일반적인 까다로운 문제 때문일 것입니다. UILabels에는 기본적으로 2 단계 레이아웃 프로세스가 필요하다는 것입니다. 자체 크기 조정 셀로 두 단계를 모두 수행하는 방법이 명확하지 않습니다.
그리고 제가 말했듯이 이것은 실제로 다른 레이아웃을위한 작업입니다. 너비를 고정하고 높이를 선택할 수 있도록하는 것이 아니라 크기가있는 항목을 배치하는 것이 흐름 레이아웃의 핵심입니다.
그리고 preferredLayoutAttributesFittingAttributes :?
preferredLayoutAttributesFittingAttributes:
방법은 훈제 청어, 나는 생각한다. 그것은 새로운 자체 크기 조정 셀 메커니즘과 함께 사용됩니다. 따라서 메커니즘이 신뢰할 수없는 한 이것은 답이 아닙니다.
그리고 systemlayoutSizeFittingSize :는 무엇입니까?
문서가 혼란 스럽습니다.
의 문서 systemLayoutSizeFittingSize:
와 systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:
모두 만 통과해야한다고 제안 UILayoutFittingCompressedSize
하고 UILayoutFittingExpandedSize
는 AS targetSize
. 그러나 메서드 서명 자체, 헤더 주석 및 함수 동작은 targetSize
매개 변수 의 정확한 값에 응답하고 있음을 나타냅니다 .
실제로 UICollectionViewFlowLayoutDelegate.estimatedItemSize
새로운 자체 크기 조정 셀 메커니즘을 사용하기 위해을 설정하면 해당 값이 targetSize로 전달되는 것처럼 보입니다. 그리고와 UILabel.systemLayoutSizeFittingSize
정확히 동일한 값을 반환하는 것 같습니다 UILabel.sizeThatFits
. 인수가 systemLayoutSizeFittingSize
대략적인 대상이고 인수가 sizeThatFits:
최대 외접 크기 여야 한다는 점을 감안할 때 이것은 의심 스럽습니다 .
더 많은 리소스
그러한 일상적인 요구 사항에 "연구 자원"이 필요하다고 생각하는 것은 슬프지만 나는 그렇게 생각합니다. 좋은 예와 토론은 다음과 같습니다.