การระบุหนึ่งมิติของเซลล์ใน UICollectionView โดยใช้เค้าโครงอัตโนมัติ


113

ใน iOS 8 UICollectionViewFlowLayoutรองรับการปรับขนาดเซลล์โดยอัตโนมัติตามขนาดเนื้อหาของตนเอง ซึ่งจะปรับขนาดเซลล์ทั้งในด้านความกว้างและความสูงตามเนื้อหา

เป็นไปได้หรือไม่ที่จะระบุค่าคงที่สำหรับความกว้าง (หรือความสูง) ของเซลล์ทั้งหมดและอนุญาตให้มิติอื่นปรับขนาดได้

สำหรับตัวอย่างง่ายๆให้พิจารณาป้ายหลายบรรทัดในเซลล์โดยมีข้อ จำกัด ในการวางตำแหน่งไว้ที่ด้านข้างของเซลล์ ป้ายกำกับหลายบรรทัดสามารถปรับขนาดได้หลายวิธีเพื่อรองรับข้อความ เซลล์ควรเติมเต็มความกว้างของมุมมองคอลเลกชันและปรับความสูงให้เหมาะสม เซลล์จะมีขนาดตามอำเภอใจและยังทำให้เกิดข้อขัดข้องเมื่อขนาดเซลล์ใหญ่กว่ามิติที่เลื่อนไม่ได้ของมุมมองคอลเลกชัน


iOS 8 แนะนำวิธีการsystemLayoutSizeFittingSize: withHorizontalFittingPriority: verticalFittingPriority:สำหรับแต่ละเซลล์ในมุมมองคอลเลกชันเค้าโครงเรียกใช้เมธอดนี้บนเซลล์โดยส่งผ่านขนาดโดยประมาณ สิ่งที่สมเหตุสมผลสำหรับฉันคือการแทนที่วิธีนี้บนเซลล์ส่งผ่านขนาดที่กำหนดและตั้งค่าข้อ จำกัด แนวนอนเป็นที่ต้องการและลำดับความสำคัญต่ำสำหรับข้อ จำกัด แนวตั้ง ด้วยวิธีนี้ขนาดแนวนอนจะถูกกำหนดให้เป็นค่าที่ตั้งไว้ในเค้าโครงและขนาดแนวตั้งสามารถยืดหยุ่นได้

สิ่งนี้:

- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
    UICollectionViewLayoutAttributes *attributes = [super preferredLayoutAttributesFittingAttributes:layoutAttributes];
    attributes.size = [self systemLayoutSizeFittingSize:layoutAttributes.size withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel];
    return attributes;
}

อย่างไรก็ตามขนาดที่ให้กลับโดยวิธีนี้เป็นเรื่องแปลกอย่างสิ้นเชิง เอกสารเกี่ยวกับวิธีนี้ไม่ชัดเจนสำหรับฉันและกล่าวถึงโดยใช้ค่าคงUILayoutFittingCompressedSize UILayoutFittingExpandedSizeที่ซึ่งเป็นเพียงแค่ขนาดศูนย์และค่าที่ค่อนข้างใหญ่

เป็นsizeพารามิเตอร์ของวิธีนี้จริงๆเพียงวิธีการที่จะผ่านในสองคงที่? ไม่มีวิธีใดที่จะบรรลุพฤติกรรมที่ฉันคาดหวังว่าจะได้รับความสูงที่เหมาะสมกับขนาดที่กำหนด?


โซลูชันทางเลือก

1) การเพิ่มข้อ จำกัด ที่จะระบุความกว้างเฉพาะสำหรับเซลล์ทำให้ได้รูปแบบที่ถูกต้อง นี่เป็นวิธีแก้ปัญหาที่ไม่ดีเนื่องจากควรตั้งค่าข้อ จำกัด ดังกล่าวเป็นขนาดของมุมมองคอลเลกชันของเซลล์ซึ่งไม่มีการอ้างอิงที่ปลอดภัย ค่าสำหรับข้อ จำกัด นั้นสามารถส่งผ่านได้เมื่อมีการกำหนดค่าเซลล์ แต่ดูเหมือนว่าจะขัดกันโดยสิ้นเชิง นอกจากนี้ยังเป็นเรื่องที่น่าอึดอัดใจเนื่องจากการเพิ่มข้อ จำกัด ลงในเซลล์โดยตรงหรือเป็นการดูเนื้อหาทำให้เกิดปัญหามากมาย

2) ใช้มุมมองตาราง มุมมองตารางจะทำงานในลักษณะนี้เนื่องจากเซลล์มีความกว้างคงที่ แต่จะไม่รองรับสถานการณ์อื่น ๆ เช่นเค้าโครง iPad ที่มีเซลล์ความกว้างคงที่ในหลายคอลัมน์

คำตอบ:


135

ดูเหมือนสิ่งที่คุณกำลังขอคือวิธีใช้ 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 แบบหลายบรรทัดต้องใช้กระบวนการจัดวางแบบสองขั้นตอน งานเสร็จสิ้นในวิธีการ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
 }

(โค้ดนี้ดัดแปลงมาจากโค้ดตัวอย่างของ Apple จากโค้ดตัวอย่างของเซสชัน WWDC2014 ใน "Advanced Collection View")

จุดสังเกตสองสามข้อ โดยใช้ layoutIfNeeded () เพื่อบังคับเลย์เอาท์ของทั้งเซลล์เพื่อคำนวณและกำหนดความกว้างของป้ายกำกับ แต่นั่นยังไม่เพียงพอ ฉันเชื่อว่าคุณต้องตั้งค่าด้วยpreferredMaxLayoutWidthเพื่อให้ป้ายกำกับใช้ความกว้างนั้นกับการออกแบบอัตโนมัติ จากนั้นคุณสามารถใช้systemLayoutSizeFittingSizeเพื่อให้เซลล์คำนวณความสูงในขณะที่คำนึงถึงป้ายกำกับ

ฉันชอบแนวทางนี้หรือไม่? ไม่มี !! รู้สึกว่าซับซ้อนเกินไปและจะจัดวางสองครั้ง แต่ตราบใดที่ประสิทธิภาพไม่กลายเป็นปัญหาฉันควรใช้เลย์เอาต์สองครั้งที่รันไทม์แทนที่จะต้องกำหนดโค้ดสองครั้งซึ่งดูเหมือนจะเป็นทางเลือกอื่นเท่านั้น

ความหวังของฉันคือในที่สุดเซลล์ปรับขนาดตัวเองจะทำงานแตกต่างกันและทั้งหมดนี้จะง่ายขึ้นมาก

ตัวอย่างโครงการที่แสดงในที่ทำงาน

แต่ทำไมไม่ใช้เซลล์ปรับขนาดตัวเองล่ะ?

ตามทฤษฎีแล้วสิ่งอำนวยความสะดวกใหม่ของ iOS8 สำหรับ "เซลล์ที่ปรับขนาดได้เอง" ควรทำให้สิ่งนี้ไม่จำเป็น หากคุณได้กำหนดเซลล์ด้วยโครงร่างอัตโนมัติ (AL) มุมมองคอลเลคชันควรฉลาดพอที่จะปล่อยให้มีขนาดและจัดวางได้อย่างถูกต้อง ในทางปฏิบัติฉันไม่เห็นตัวอย่างใด ๆ ที่ทำให้สามารถใช้งานกับป้ายกำกับหลายบรรทัดได้ ผมคิดว่านี่คือส่วนหนึ่งเป็นเพราะกลไกของเซลล์ตัวเองขนาดยังคงเป็นรถ

แต่ฉันพนันได้เลยว่าส่วนใหญ่เป็นเพราะความยุ่งยากตามปกติของเค้าโครงอัตโนมัติและป้ายกำกับซึ่งก็คือ UILabels ต้องการกระบวนการจัดวางแบบสองขั้นตอนโดยทั่วไป ยังไม่ชัดเจนสำหรับฉันว่าคุณสามารถดำเนินการทั้งสองขั้นตอนด้วยเซลล์ปรับขนาดตัวเองได้อย่างไร

และอย่างที่บอกว่านี่เป็นงานสำหรับเค้าโครงที่แตกต่างกันจริงๆ เป็นส่วนหนึ่งของสาระสำคัญของเค้าโครงโฟลว์ที่วางตำแหน่งของสิ่งต่างๆที่มีขนาดแทนที่จะแก้ไขความกว้างและให้พวกเขาเลือกความสูง

แล้วที่ต้องการคืออะไร?

preferredLayoutAttributesFittingAttributes:วิธีเป็นปลาชนิดหนึ่งสีแดงผมคิดว่า นั่นคือจะใช้กับกลไกเซลล์ปรับขนาดตัวเองใหม่เท่านั้น นี่ไม่ใช่คำตอบตราบใดที่กลไกนั้นเชื่อถือไม่ได้

และเกิดอะไรขึ้นกับ systemlayoutSizeFittingSize :?

คุณคิดถูกแล้วที่เอกสารสับสน

เอกสารsystemLayoutSizeFittingSize:และsystemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:ทั้งคู่แนะนำว่าคุณควรผ่านUILayoutFittingCompressedSizeและUILayoutFittingExpandedSizeเป็นไฟล์targetSize. อย่างไรก็ตามลายเซ็นเมธอดเองความคิดเห็นส่วนหัวและลักษณะการทำงานของฟังก์ชันระบุว่าพวกเขากำลังตอบสนองต่อค่าที่แน่นอนของtargetSizeพารามิเตอร์

ในความเป็นจริงหากคุณตั้งค่าUICollectionViewFlowLayoutDelegate.estimatedItemSizeเพื่อเปิดใช้งานกลไกการปรับขนาดเซลล์ด้วยตนเองค่านั้นจะถูกส่งผ่านเป็น targetSize และดูเหมือนว่าจะกลับค่าเดียวกันกับUILabel.systemLayoutSizeFittingSize UILabel.sizeThatFitsสิ่งนี้น่าสงสัยเนื่องจากว่าอาร์กิวเมนต์ที่systemLayoutSizeFittingSizeควรจะเป็นเป้าหมายคร่าวๆและอาร์กิวเมนต์sizeThatFits:ควรมีขนาด จำกัด สูงสุด

แหล่งข้อมูลเพิ่มเติม

แม้ว่าจะเป็นเรื่องน่าเศร้าที่คิดว่าข้อกำหนดประจำเช่นนี้ควรต้องการ "แหล่งข้อมูลการวิจัย" แต่ฉันคิดว่ามันเป็น ตัวอย่างและการอภิปรายที่ดี ได้แก่ :


จะตั้งกรอบกลับไปที่กรอบเก่าทำไม?
vrwim

ฉันทำเช่นนั้นเพื่อให้สามารถใช้ฟังก์ชันนี้เพื่อกำหนดขนาดเลย์เอาต์ที่ต้องการของมุมมองเท่านั้นโดยไม่ส่งผลต่อสถานะเค้าโครงของมุมมองที่คุณเรียกใช้ในทางปฏิบัติสิ่งนี้ไม่เกี่ยวข้องหากคุณถือมุมมองนี้เพียงเพื่อจุดประสงค์ ทำการคำนวณเค้าโครง นอกจากนี้สิ่งที่ฉันทำที่นี่ยังไม่เพียงพอ การกู้คืนเฟรมเพียงอย่างเดียวไม่ได้คืนค่ากลับเป็นสถานะก่อนหน้าจริง ๆ เนื่องจากการใช้เลย์เอาต์อาจมีการแก้ไขเลย์เอาต์ของมุมมองย่อย
สาหร่าย

1
นี่เป็นความยุ่งเหยิงสำหรับบางสิ่งที่ผู้คนทำมาตั้งแต่ ios6 Apple มักจะออกมาด้วยวิธีใดวิธีหนึ่ง แต่ก็ไม่ถูกต้องนัก
GregP

1
คุณสามารถสร้างอินสแตนซ์เซลล์ขนาดใน viewDidLoad ด้วยตนเองและกดค้างไว้เพื่อดูคุณสมบัติที่กำหนดเอง เป็นเพียงเซลล์เดียวราคาถูก เนื่องจากคุณใช้เพื่อปรับขนาดเท่านั้นและคุณกำลังจัดการการโต้ตอบทั้งหมดด้วยจึงไม่จำเป็นต้องมีส่วนร่วมในกลไกการนำเซลล์กลับมาใช้ใหม่ของมุมมองคอลเลกชันดังนั้นคุณจึงไม่จำเป็นต้องรับจากมุมมองคอลเล็กชันเอง
สาหร่าย

1
เซลล์ปรับขนาดตัวเองยังคงแตกอยู่บนโลกได้อย่างไร!
Reuben Scratton

50

มีวิธีที่สะอาดกว่าคำตอบอื่น ๆ ที่นี่และได้ผลดี ควรมีประสิทธิภาพ (มุมมองคอลเลคชันโหลดเร็วไม่มีการส่งผ่านเลย์เอาต์อัตโนมัติที่ไม่จำเป็น ฯลฯ ) และไม่มี 'ตัวเลขวิเศษ' เช่นความกว้างมุมมองคอลเลกชันคงที่ การเปลี่ยนขนาดมุมมองคอลเลกชันเช่นเมื่อหมุนแล้วทำให้เค้าโครงไม่ถูกต้องก็น่าจะได้ผลเช่นกัน

1. สร้างคลาสย่อยโครงร่างโฟลว์ต่อไปนี้

class HorizontallyFlushCollectionViewFlowLayout: UICollectionViewFlowLayout {

    // Don't forget to use this class in your storyboard (or code, .xib etc)

    override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
        let attributes = super.layoutAttributesForItemAtIndexPath(indexPath)?.copy() as? UICollectionViewLayoutAttributes
        guard let collectionView = collectionView else { return attributes }
        attributes?.bounds.size.width = collectionView.bounds.width - sectionInset.left - sectionInset.right
        return attributes
    }

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let allAttributes = super.layoutAttributesForElementsInRect(rect)
        return allAttributes?.flatMap { attributes in
            switch attributes.representedElementCategory {
            case .Cell: return layoutAttributesForItemAtIndexPath(attributes.indexPath)
            default: return attributes
            }
        }
    }
}

2. ลงทะเบียนมุมมองคอลเลกชันของคุณสำหรับการปรับขนาดอัตโนมัติ

// The provided size should be a plausible estimate of the actual
// size. You can set your item size in your storyboard
// to a good estimate and use the code below. Otherwise,
// you can provide it manually too, e.g. CGSize(width: 100, height: 100)

flowLayout.estimatedItemSize = flowLayout.itemSize

3. ใช้ความกว้างที่กำหนดไว้ล่วงหน้า + ความสูงที่กำหนดเองในคลาสย่อยของเซลล์ของคุณ

override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
    layoutAttributes.bounds.size.height = systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
    return layoutAttributes
}

8
นี่คือคำตอบที่ดีที่สุดที่ฉันพบในหัวข้อการปรับขนาดตัวเอง ขอบคุณจอร์แดน!
haik.ampardjian

1
ไม่สามารถใช้งานได้บน iOS (9.3) ในวันที่ 10 ทำงานได้ดี แอปพลิเคชันขัดข้องใน _updateVisibleCells (การเรียกซ้ำไม่สิ้นสุด?) openradar.me/25303115
cristiano2lopes

@ cristiano2lopes มันทำงานได้ดีสำหรับฉันบน iOS 9.3 ตรวจสอบให้แน่ใจว่าคุณได้ประมาณการที่เหมาะสมและตรวจสอบว่าข้อ จำกัด ของเซลล์ของคุณได้รับการตั้งค่าเพื่อแก้ไขขนาดที่ถูกต้อง
Jordan Smith

มันใช้งานได้น่าทึ่ง ฉันใช้ Xcode 8 และทดสอบใน iOS 9.3.3 (อุปกรณ์จริง) และไม่ผิดพลาด! ใช้งานได้ดี ตรวจสอบให้แน่ใจว่าการประมาณของคุณดี!
hahmed

1
@JordanSmith สิ่งนี้ไม่ทำงานใน iOS 10 คุณมีตัวอย่างการสาธิตหรือไม่ ฉันได้ลองทำหลายสิ่งหลายอย่างเพื่อดูความสูงของเซลล์ตามเนื้อหา แต่ไม่มีอะไรเลย ฉันใช้การจัดวางอัตโนมัติใน iOS 10 และใช้ swift 3
Ekta Padaliya

6

วิธีง่ายๆในการทำใน iOS 9 ด้วยโค้ดไม่กี่บรรทัด - ตัวอย่างแนวนอน (กำหนดความสูงเป็นความสูงของมุมมองคอลเลคชัน):

เริ่มต้นเค้าโครงมุมมองคอลเลกชันของคุณด้วยการestimatedItemSizeเปิดใช้งานการปรับขนาดเซลล์ด้วยตนเอง:

self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.estimatedItemSize = CGSizeMake(1, 1);

ใช้งาน Collection View Layout Delegate (ใน View Controller เกือบตลอดเวลา) collectionView:layout:sizeForItemAtIndexPath:,. เป้าหมายคือการกำหนดความสูงคงที่ (หรือความกว้าง) ให้กับมิติข้อมูลมุมมองคอลเลคชัน ค่า 10 อาจเป็นอะไรก็ได้ แต่คุณควรตั้งเป็นค่าที่ไม่ทำลายข้อ จำกัด :

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout *)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return CGSizeMake(10, CGRectGetHeight(collectionView.bounds));
}

แทนที่preferredLayoutAttributesFittingAttributes:เมธอดเซลล์ที่คุณกำหนดเองส่วนนี้จะคำนวณความกว้างของเซลล์แบบไดนามิกตามข้อ จำกัด ของโครงร่างอัตโนมัติและความสูงที่คุณเพิ่งตั้งค่าไว้:

- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
    UICollectionViewLayoutAttributes *attributes = [layoutAttributes copy];
    float desiredWidth = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].width;
    CGRect frame = attributes.frame;
    frame.size.width = desiredWidth;
    attributes.frame = frame;
    return attributes;
}

คุณรู้หรือไม่ว่าบน iOS9 วิธีที่ง่ายกว่านี้ทำงานได้อย่างถูกต้องเมื่อเซลล์มีหลายบรรทัดUILabelซึ่งมีผลต่อความสูงที่แท้จริงของเซลล์ นี่เป็นกรณีทั่วไปที่ต้องใช้ backflips ทั้งหมดที่ฉันอธิบาย ฉันสงสัยว่า iOS ได้แก้ไขแล้วหรือไม่ตั้งแต่คำตอบของฉัน
สาหร่าย

2
@algal น่าเศร้าที่คำตอบคือไม่
เจมส์

4

ลองกำหนดความกว้างของคุณในแอตทริบิวต์เค้าโครงที่ต้องการ:

- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
    UICollectionViewLayoutAttributes *attributes = [[super preferredLayoutAttributesFittingAttributes:layoutAttributes] copy];
    CGSize newSize = [self systemLayoutSizeFittingSize:CGSizeMake(FIXED_WIDTH,layoutAttributes.size) withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel];
    CGRect newFrame = attr.frame;
    newFrame.size.height = size.height;
    attr.frame = newFrame;
    return attr;
}

โดยปกติแล้วคุณต้องการให้แน่ใจว่าคุณตั้งค่าเค้าโครงของคุณอย่างถูกต้องเพื่อ:

UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *) self.collectionView.collectionViewLayout;
flowLayout.estimatedItemSize = CGSizeMake(FIXED_WIDTH, estimatedHeight)];

นี่คือสิ่งที่ฉันใส่ในGithubที่ใช้เซลล์ความกว้างคงที่และรองรับประเภทไดนามิกดังนั้นความสูงของเซลล์จะอัปเดตเมื่อขนาดตัวอักษรของระบบเปลี่ยนแปลงไป


ในตัวอย่างนี้คุณเรียก systemLayoutSizeFittingSize: คุณได้จัดการเพื่อให้สิ่งนี้ทำงานได้โดยไม่ต้องตั้งค่าที่ต้องการ MaxLayoutWidth ไว้ที่ไหนสักแห่งหรือไม่? จากประสบการณ์ของฉันหากคุณไม่ได้ตั้งค่าที่ต้องการ MaxLayoutWidth คุณจะต้องสร้างเลย์เอาต์ด้วยตนเองเพิ่มเติมเช่นโดยการลบล้าง sizeThatFits: ด้วยการคำนวณที่สร้างตรรกะของข้อ จำกัด การจัดวางอัตโนมัติที่กำหนดไว้ที่อื่น
สาหร่าย

@algal recommendedMaxLayoutWidth เป็นทรัพย์สินใน UILabel? ไม่แน่ใจว่ามีความเกี่ยวข้องอย่างไร?
Daniel Galasko

อ๊ะ! จุดดีไม่ใช่! ฉันไม่เคยจัดการสิ่งนี้ด้วย UITextView จึงไม่ทราบว่า API ของพวกเขาแตกต่างกันที่นั่น ดูเหมือนว่า UITextView.systemLayoutSizeFittingSize เคารพเฟรมเนื่องจากไม่มี intrinsicContentSize แต่ UILabel.systemLayoutSizeFittingSize จะไม่สนใจเฟรมเนื่องจากมี intrinsicContentSize ดังนั้นจึงจำเป็นต้องใช้ MaxLayoutWidth เพื่อให้ได้รับ intrinsicContentSize เพื่อแสดงความสูงที่ห่อไว้กับ AL ดูเหมือนว่า UITextView จะต้องใช้สองรอบหรือเลย์เอาต์ด้วยตนเอง แต่ฉันไม่คิดว่าฉันเข้าใจทั้งหมดนี้ทั้งหมด
สาหร่าย

@algal ฉันเห็นด้วยฉันเคยประสบปัญหาขนดกเมื่อใช้ UILabel การตั้งค่าความกว้างที่ต้องการช่วยแก้ปัญหาได้ แต่จะเป็นการดีหากมีวิธีการแบบไดนามิกมากขึ้นเนื่องจากความกว้างนี้จำเป็นต้องปรับขนาดด้วย superview โครงการส่วนใหญ่ของฉันไม่ได้ใช้คุณสมบัติที่ต้องการฉันมักจะใช้สิ่งนั้นเป็นการแก้ไขอย่างรวดเร็วเนื่องจากมีปัญหาที่ลึกกว่าเสมอ
Daniel Galasko

0

ใช่สามารถทำได้โดยใช้การจัดวางอัตโนมัติโดยทางโปรแกรมและโดยการตั้งค่าข้อ จำกัด ในสตอรีบอร์ดหรือ xib คุณต้องเพิ่มข้อ จำกัด เพื่อให้ขนาดความกว้างคงที่และตั้งค่าความสูงให้มากกว่าหรือเท่ากับ
http://www.thinkandbuild.it/learn-to-love-auto-layout-programmatically/

http://www.cocoanetics.com/2013/08/variable-sized-items-in-uicollectionview/
หวังว่านี่จะเป็น เป็นประโยชน์และแก้ปัญหาของคุณ


3
สิ่งนี้ควรใช้ได้กับค่าความกว้างสัมบูรณ์ แต่ในกรณีที่คุณต้องการให้เซลล์มีความกว้างเต็มของมุมมองคอลเลกชันเสมอจะไม่ทำงาน
Michael Waterfall

@MichaelWaterfall ฉันจัดการเพื่อให้มันทำงานเต็มความกว้างโดยใช้ AutoLayout ฉันได้เพิ่มข้อ จำกัด ด้านความกว้างในมุมมองเนื้อหาของฉันภายในเซลล์ จากนั้นในcellForItemAtIndexPathการปรับปรุงข้อ จำกัดcell.constraintItemWidth.constant = UIScreen.main.bounds.width: แอปของฉันใช้งานแบบพกพาเท่านั้น คุณอาจต้องการreloadDataหลังจากเปลี่ยนการวางแนวเพื่ออัปเดตข้อ จำกัด
Flitskikker
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.