2012-10-10 34 views
23

有沒有人實現了iOS 6 UICollectionView的裝飾視圖? 不可能找到關於在網絡上實現裝飾視圖的任何教程。基本上在我的應用程序中,我有多個部分,我只想在每個部分後面顯示裝飾視圖。這應該很容易實現,但我沒有運氣。這使我瘋狂......謝謝。UICollectionView裝飾視圖

+0

您是否檢查過本教程http://www.raywenderlich.com/22324/beginning-uicollectionview-in-ios-6-part-12。我不確定他們是否已經這樣做。但值得在那裏查看。 – iDev

+0

對不起,但你能解釋一下裝飾視圖的含義嗎? – geraldWilliam

+0

@geraldWilliam,在我的評論中查看上面的鏈接。他們有一個很好的教程解釋。 – iDev

回答

22

我得到這個工作有一個自定義佈局有以下:

創建UICollectionReusableView的子類,例如一個的UIImageView添加到它:在viewDidLoad中

@implementation AULYFloorPlanDecorationViewCell 

- (id)initWithFrame:(CGRect)frame 
{ 
    self = [super initWithFrame:frame]; 
    if (self) { 
     UIImage *backgroundImage = [UIImage imageNamed:@"Layout.png"]; 
     UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame]; 
     imageView.image = backgroundImage; 
     [self addSubview:imageView]; 
    } 
    return self; 
} 

@end 

然後在你的控制器註冊這個子類用下面的代碼(替換您的自定義佈局代碼)

AULYAutomationObjectLayout *automationLayout = (AULYAutomationObjectLayout *)self.collectionView.collectionViewLayout; 
[automationLayout registerClass:[AULYFloorPlanDecorationViewCell class] forDecorationViewOfKind:@"FloorPlan"]; 

在您的自定義佈局然後實現跟隨着摹方法(或類似):

- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath 
{ 
    UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath]; 
    layoutAttributes.frame = CGRectMake(0.0, 0.0, self.collectionViewContentSize.width, self.collectionViewContentSize.height); 
    layoutAttributes.zIndex = -1; 
    return layoutAttributes; 
} 

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 
{ 
    NSMutableArray *allAttributes = [[NSMutableArray alloc] initWithCapacity:4]; 

    [allAttributes addObject:[self layoutAttributesForDecorationViewOfKind:@"FloorPlan" atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]]; 

    for (NSInteger i = 0; i < [self.collectionView numberOfItemsInSection:0]; i++) 
    { 
     NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; 
     UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForItemAtIndexPath:indexPath]; 
     [allAttributes addObject:layoutAttributes]; 
    } 
    return allAttributes; 
} 

人們似乎沒有它的文件,但下面的文件讓我在正確的軌道上:Collection View Programming Guide for iOS

UPDATE:這可能是更好的繼承UICollectionReusableView了裝修視圖,而不是UICollectionViewCell

+0

感謝這讓我走上了正軌。看起來你只是爲第0節顯示一個裝飾視圖。我的集合視圖有多個部分。我仍然需要弄清楚如何爲不同數量的項目顯示每個部分的相同裝飾視圖。 – vtruong

+0

是的,我只顯示一個裝飾視圖,但您可以通過在layoutAttributesForElementsInRect中添加更多行來顯示更多裝飾視圖:'[allAttributes addObject:[self layoutAttributesForDecorationViewOfKind:@「FloorPlan」atIndexPath:[NSIndexPath indexPathForItem:0 inSection:1]] ];'(你可以在第2,3,4節中進一步添加),然後你必須確保設置佈局屬性,使得該部分的邊界是正確的。要找出部分的數量和多少他們包含你的元素應該能夠訪問'self.collectionView。dataSource' – dominikk

+1

我是否需要繼承'UICollectionViewLayout'才能使用裝飾視圖?它可以從FlowLayout完成嗎? –

5

這裏是如何做到在MonoTouch的:

public class DecorationView : UICollectionReusableView 
{ 
    private static NSString classId = new NSString ("DecorationView"); 
    public static NSString ClassId { get { return classId; } } 

    UIImageView blueMarble; 

    [Export("initWithFrame:")] 
    public DecorationView (RectangleF frame) : base(frame) 
    { 
     blueMarble = new UIImageView (UIImage.FromBundle ("bluemarble.png")); 

     AddSubview (blueMarble);  
    } 
} 


public class SimpleCollectionViewController : UICollectionViewController 
{ 
    public override void ViewDidLoad() 
    { 
     base.ViewDidLoad(); 
     //Register the cell class (code for AnimalCell snipped) 
     CollectionView.RegisterClassForCell (typeof(AnimalCell), AnimalCell.ClassId); 
     //Register the supplementary view class (code for SideSupplement snipped) 
     CollectionView.RegisterClassForSupplementaryView (typeof(SideSupplement), UICollectionElementKindSection.Header, SideSupplement.ClassId); 

     //Register the decoration view 
     CollectionView.CollectionViewLayout.RegisterClassForDecorationView (typeof(DecorationView), DecorationView.ClassId); 
    } 

//...snip... 
} 

public class LineLayout : UICollectionViewFlowLayout 
{ 
    public override UICollectionViewLayoutAttributes[] LayoutAttributesForElementsInRect (RectangleF rect) 
    { 
     var array = base.LayoutAttributesForElementsInRect (rect); 
     /* 
     ...snip content relating to cell layout... 
     */ 
     //Add decoration view 
     var attributesWithDecoration = new List<UICollectionViewLayoutAttributes> (array.Length + 1); 
     attributesWithDecoration.AddRange (array); 
     var decorationIndexPath = NSIndexPath.FromIndex (0); 
     var decorationAttributes = LayoutAttributesForDecorationView (DecorationView.ClassId, decorationIndexPath); 
     attributesWithDecoration.Add (decorationAttributes); 

     var extended = attributesWithDecoration.ToArray<UICollectionViewLayoutAttributes>(); 

     return extended; 
    } 

    public override UICollectionViewLayoutAttributes LayoutAttributesForDecorationView (NSString kind, NSIndexPath indexPath) 
    { 
     var layoutAttributes = UICollectionViewLayoutAttributes.CreateForDecorationView (kind, indexPath); 
     layoutAttributes.Frame = new RectangleF (0, 0, CollectionView.ContentSize.Width, CollectionView.ContentSize.Height); 
     layoutAttributes.ZIndex = -1; 
     return layoutAttributes; 
    } 
//...snip... 
} 

與最終resul牛逼類似於: enter image description here

35

這裏的一個集合視圖佈局裝飾視圖教程在Swift(這是Swift 3,Xcode 8種子6)。


裝飾視圖不是UICollectionView功能;它們基本屬於UICollectionViewLayout。沒有UICollectionView方法(或委託或數據源方法)提到裝飾視圖。 UICollectionView對它們一無所知;它只是做它被告知的事情。

要提供任何裝飾視圖,您需要一個UICollectionViewLayout子類;這個子類可以自由定義自己的屬性和委託協議方法來自定義裝飾視圖的配置方式,但這完全取決於你自己。

爲了進行說明,我將繼承UICollectionViewFlowLayout,在集合視圖的內容矩形的頂部添加一個標題標籤。這可能是一個裝飾視圖的愚蠢使用,但它完美地說明了基本原則。爲了簡單起見,我將首先對整個事物進行硬編碼,讓客戶無法定製此視圖的任何方面。

有四個步驟,以在一個子類佈局實現裝飾視圖:

  1. 定義UICollectionReusableView子類。

  2. 註冊UICollectionReusableView子類的佈局(集合視圖),通過調用register(_:forDecorationViewOfKind:)。佈局的初始化器是做這件事的好地方。

  3. 實施layoutAttributesForDecorationView(ofKind:at:)返回佈局屬性該位置UICollectionReusableView。要構建佈局屬性,請調用init(forDecorationViewOfKind:with:)並配置屬性。

  4. 覆蓋layoutAttributesForElements(in:)以便layoutAttributesForDecorationView(ofKind:at:)的結果包含在返回的數組中。

的最後一步是什麼原因導致出現在集合視圖裝修視圖。當集合視圖調用layoutAttributesForElements(in:)時,它會發現生成的數組包含指定類型裝飾視圖的佈局屬性。集合視圖對裝飾視圖一無所知,所以它回到了佈局,要求這種裝飾視圖的實際實例。您已經註冊了這種裝飾視圖以對應於您的UICollectionReusableView子類,因此您的UICollectionReusableView子類將被實例化並返回該實例,並且集合視圖將根據佈局屬性對其進行定位。

那麼讓我們按照步驟。定義UICollectionReusableView子類:

class MyTitleView : UICollectionReusableView { 
    weak var lab : UILabel! 
    override init(frame: CGRect) { 
     super.init(frame:frame) 
     let lab = UILabel(frame:self.bounds) 
     self.addSubview(lab) 
     lab.autoresizingMask = [.flexibleWidth, .flexibleHeight] 
     lab.font = UIFont(name: "GillSans-Bold", size: 40) 
     lab.text = "Testing" 
     self.lab = lab 
    } 
    required init?(coder aDecoder: NSCoder) { 
     fatalError("init(coder:) has not been implemented") 
    } 
} 

現在,我們把我們的UICollectionViewLayout子類,我會打電話給MyFlowLayout。我們在佈局的初始化程序中註冊MyTitleView;我還定義了我需要的剩餘步驟一些私人性質:

private let titleKind = "title" 
private let titleHeight : CGFloat = 50 
private var titleRect : CGRect { 
    return CGRect(10,0,200,self.titleHeight) 
} 
override init() { 
    super.init() 
    self.register(MyTitleView.self, forDecorationViewOfKind:self.titleKind) 
} 

實施layoutAttributesForDecorationView(ofKind:at:)

override func layoutAttributesForDecorationView(
    ofKind elementKind: String, at indexPath: IndexPath) 
    -> UICollectionViewLayoutAttributes? { 
     if elementKind == self.titleKind { 
      let atts = UICollectionViewLayoutAttributes(
       forDecorationViewOfKind:self.titleKind, with:indexPath) 
      atts.frame = self.titleRect 
      return atts 
     } 
     return nil 
} 

覆蓋layoutAttributesForElements(in:);這裏的索引路徑是任意的(我忽略了它在上面的代碼):

override func layoutAttributesForElements(in rect: CGRect) 
    -> [UICollectionViewLayoutAttributes]? { 
     var arr = super.layoutAttributesForElements(in: rect)! 
     if let decatts = self.layoutAttributesForDecorationView(
      ofKind:self.titleKind, at: IndexPath(item: 0, section: 0)) { 
       if rect.contains(decatts.frame) { 
        arr.append(decatts) 
       } 
     } 
     return arr 
} 

這工作!讀取「測試」的標題標籤出現在集合視圖的頂部。


現在我將展示如何使標籤可定製。我們將允許客戶設置一個確定標題的屬性,而不是標題「測試」。我給我的佈局子類公共title屬性:

class MyFlowLayout : UICollectionViewFlowLayout { 
    var title = "" 
    // ... 
} 

誰使用這種佈局應該設置該屬性。例如,假設此收集視圖顯示美國50個州:

func setUpFlowLayout(_ flow:UICollectionViewFlowLayout) { 
    flow.headerReferenceSize = CGSize(50,50) 
    flow.sectionInset = UIEdgeInsetsMake(0, 10, 10, 10) 
    (flow as? MyFlowLayout)?.title = "States" // * 
} 

我們現在來到一個奇怪的難題。我們的佈局有一個title屬性,其價值需要以某種方式傳達給我們的MyTitleView實例。但是,什麼時候可能發生?我們不負責實例化MyTitleView;它會自動發生,當收集視圖要求在幕後實例時。沒有任何時候MyFlowLayout實例和MyTitleView實例會合。

解決方案是使用佈局屬性作爲信使。 MyFlowLayout永遠不符合MyTitleView,但它確實創建了傳遞給集合視圖的佈局屬性對象來配置MyFlowLayout。所以佈局屬性對象就像一個信封。通過繼承UICollectionViewLayoutAttributes,我們可以在信封包含任何我們喜歡的信息 - 例如標題:

class MyTitleViewLayoutAttributes : UICollectionViewLayoutAttributes { 
    var title = "" 
} 

有我們的信封!現在我們重寫我們的實現layoutAttributesForDecorationView。當我們實例化佈局對象的屬性,我們實例子類,並設置其屬性title

override func layoutAttributesForDecorationView(
    ofKind elementKind: String, at indexPath: IndexPath) -> 
    UICollectionViewLayoutAttributes? { 
     if elementKind == self.titleKind { 
      let atts = MyTitleViewLayoutAttributes(// * 
       forDecorationViewOfKind:self.titleKind, with:indexPath) 
      atts.title = self.title // * 
      atts.frame = self.titleRect 
      return atts 
     } 
     return nil 
} 

最後,在MyTitleView,我們實行apply(_:)方法。這將在集合視圖配置裝飾視圖時調用 - 以佈局屬性對象爲參數!因此,我們拔出title,並用它作爲我們的標籤的文本:

class MyTitleView : UICollectionReusableView { 
    weak var lab : UILabel! 
    // ... the rest as before ... 
    override func apply(_ atts: UICollectionViewLayoutAttributes) { 
     if let atts = atts as? MyTitleViewLayoutAttributes { 
      self.lab.text = atts.title 
     } 
    } 
} 

可以很容易地看到你可能會延長例子做出這樣的標籤功能,如字體和高度可定製的。由於我們是UICollectionViewFlowLayout的子類,因此可能還需要進一步修改,以通過按下其他元素爲裝飾視圖留出空間。另外,從技術上講,我們應該在MyTitleView中重寫isEqual(_:)以區分不同的標題。所有這些都是爲了讀者的練習。

+0

我不明白爲什麼這不是最好的答案!好東西。 –

+0

@GazLong謝謝!還有更多來自哪裏;它直接從我的書中剪切並粘貼。 – matt

+0

@matt非常感謝您使用佈局屬性作爲信使與裝飾視圖溝通的部分!這是一個有趣的API - 你不直接實例化或參考裝飾視圖。 –