UICollectionView Tutorial Part 2:Reusable Views and Cell Selection

在上一篇中,你学到了如何来用UICollectionView完成一个网格相册.
在本篇中,你将继续来学习collection views的更多用法.继续使用上一篇的项目或者可以在这里下载(你需要上一篇提到的新的API key).

  • 添加标头
    这个应用每个搜索结果都有一个section.可以在搜索结果之前添加一个标头,以便更好地显示照片信息.
    使用UICollectionReusableView来创建标头.这个类是collection view cell的一种(实际上,cells继承于此类),但用法却同headers或footers相似.
    这个view可以在storyboard里创建并可以连接到你的类中.新建一个UICollectionReusableView的子类,命名为FlickrPhotoHeaderView.
    在collection view的Attributes Inspector里面勾选Section Header:

UICollectionView Tutorial Part 2:Reusable Views and Cell Selection_第1张图片
Screen-Shot-2012-09-23-at-3.08.56-PM.png

你会发现"Collection Reusable View"将自动添加到Collection View的下面.选择Collection Reusable View后就可以添加子视图了.
你可以通过拖动Collection Reusable View的底部来使其变为90pixels高,便于有更多的空间.(你也可以在属性面板里改变view的尺寸)
在header view的中心添加一个label控件.将字体调为System 32.0并在alignment menu里添加水平和竖直相关的约束,更新frame:

UICollectionView Tutorial Part 2:Reusable Views and Cell Selection_第2张图片
Labelalignments.png

选择头视图,将其Class设为FlickrPhotoHeaderView.
设置背景颜色为90%的白色,视图的Identifier设为FlickrPhotoHeaderView.这个identifier将会在下面用到.
点击Assistant editor,确保FlickrPhotoHeaderView.swift打开,连线拖拽label到类中.命名为label:

class FlickrPhotoHeaderView: UICollectionReusableView {
  @IBOutlet weak var label: UILabel!
}

如果现在就运行程序,你将看不到header(或者将是个仅有个"Label"文字的空白页).你需要实现另外的一个datasource 方法.打开FlickrPhotosViewController.swift添加UICollectionViewDataSource扩展:

override func collectionView(collectionView: UICollectionView,
  viewForSupplementaryElementOfKind kind: String,
  atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
    //1
    switch kind {
      //2
      case UICollectionElementKindSectionHeader:
        //3
        let headerView =
        collectionView.dequeueReusableSupplementaryViewOfKind(kind,
          withReuseIdentifier: "FlickrPhotoHeaderView",
          forIndexPath: indexPath)
          as! FlickrPhotoHeaderView
        headerView.label.text = searches[indexPath.section].searchTerm
        return headerView
      default:
        //4
        assert(false, "Unexpected element kind")
    }
}

这个方法和cellForItemAtIndexPath相似,但却视为额外的视图所用的.下面是以上代码的释义:

  1. kind参数由布局对象提供并标明是哪个额外的视图.
  2. UICollectionElementKindSectionHeader为flow layout提供的一种额外视图.通过添加storyboard中的属性来告诉flow layout需要额外的视图,因此添加了个section header.当然也有UICollectionElementKindSectionFooter,但现在并没有用到.如果你使用的不是flow layout,并不会这么容易就添加header和footer.
  3. header通过storyboard中的identifier来标示.原理同cell相似.label上的文字被设置为相关的搜索项目.
  4. 断言在这里是提醒后来的开发者(或者是将来的你),这里并不想得到除header view外的对象.

运行程序,你会看到UI快完成了.如果你进行了多个搜索,你将会得到section headers来很好地分隔你的搜索结果.翻转设配你会发现无需额外的工作,各个布局都看起来很不错.

UICollectionView Tutorial Part 2:Reusable Views and Cell Selection_第3张图片
collectionviewheaders-666x500.png
  • Cell的交互
    在最后,你将学到cell的一些交互.你将完成两种不同的操作.一种是放大图片,一种是多选图片来分享.

  • 单个选择
    Collection能够通过动画来变化它的布局.第一个任务是点击时图片放大.
    首先,你需要添加一个属性来表示你点击的cell.打开FlickrPhotosViewController.swift,添加如下代码:

//1
var largePhotoIndexPath : NSIndexPath? {
didSet {
  //2
  var indexPaths = [NSIndexPath]()
  if largePhotoIndexPath != nil {
    indexPaths.append(largePhotoIndexPath!)
  }
  if oldValue != nil {
    indexPaths.append(oldValue!)
  }
  //3
      collectionView?.performBatchUpdates({
        self.collectionView?.reloadItemsAtIndexPaths(indexPaths)
        return
        }){
          completed in
          //4
          if self.largePhotoIndexPath != nil {
            self.collectionView?.scrollToItemAtIndexPath(
              self.largePhotoIndexPath!,
              atScrollPosition: .CenteredVertically,
              animated: true)
          }
      }
  }
}

下面是代码的释义:

  1. largePhotoIndexPath为点击图片的index path.
  2. 当此属性被更新时,collection view需要更新.didSet属性表明这是个安全的地方来处理这些更新.如果用户已经点击了一幅图片后再点击另一幅,或者点击相同一副图两次则需要缩放动画,这时需要两个cells重载.
  3. 更新动画完成后,最好将放大的图片放在屏幕中央.

"如何来增大Cell?"待会告诉你.
点击cell时会将collection view选中.你将通过设置的largeIndexPath属性来获取你点击的cell,但你并不希望这个cell真正被选中,当你使用多选时这将会是你迷惑.collection view 通过代理方法来判断是否选中了一个cell.仍然在FlickrPhotosViewController.swift添加一个新的扩展方法来增加collection view的代理方法,如下:

extension FlickrPhotosViewController : UICollectionViewDelegate {
 
  override func collectionView(collectionView: UICollectionView,
    shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
      if largePhotoIndexPath == indexPath {
        largePhotoIndexPath = nil
      }
      else {
        largePhotoIndexPath = indexPath
      }
      return false
  }
}

这个方法相当简单.如果这个cell已经是大图了,设置largePhotoIndexPath为nil,相反地,设置其index path为所点击的.这将会调用之前你添加的属性观察者来使collection view更新重载.
要实现点击cell方法图片,需要修改flow layout的代理方法sizeForItemAtIndexPath.替换成一下代码:

func collectionView(collectionView: UICollectionView,
  layout collectionViewLayout: UICollectionViewLayout,
  sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
 
  let flickrPhoto = photoForIndexPath(indexPath)
 
  // New code
  if indexPath == largePhotoIndexPath {
    var size = collectionView.bounds.size
    size.height -= topLayoutGuide.length
    size.height -= (sectionInsets.top + sectionInsets.right)
    size.width -= (sectionInsets.left + sectionInsets.right)
    return flickrPhoto.sizeToFillWidthOfSize(size)
  }
  // Previous code
  if var size = flickrPhoto.thumbnail?.size {
    size.width += 10
    size.height += 10
    return size
  }
  return CGSize(width: 100, height: 100)
}

添加的方法用来计算放大后的尺寸.
没必要更大的cell除非你有更大的图片需要真实.
在Main.storyboard的collection view cell里面image view上添加一个activity indicator.将这个activity indicator 的Style设置为Large White,勾选Hides When Stopped.将它放于cell的中央,添加约束.

UICollectionView Tutorial Part 2:Reusable Views and Cell Selection_第4张图片
cellcontents.png

将activity indicator在FlickrPhotoCell.swift中添加对应的outlet,命名为activityIndicator:

  @IBOutlet weak var activityIndicator: UIActivityIndicatorView!

然后在FlickrPhotoCell.swift中添加如下代码来控制cell的背景色:

override func awakeFromNib() {
  super.awakeFromNib()
  self.selected = false
}
 
override var selected : Bool {
  didSet {
    self.backgroundColor = selected ? themeColor : UIColor.blackColor()
  }
}

最后需要更新FlickrPhotosCollectionViewController.swift里的cellForItemAtIndexPath:

override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
 
  let cell = collectionView.dequeueReusableCellWithReuseIdentifier(
    reuseIdentifier, forIndexPath: indexPath) as! FlickrPhotoCell
  let flickrPhoto = photoForIndexPath(indexPath)
 
  //1
  cell.activityIndicator.stopAnimating()
 
  //2
  if indexPath != largePhotoIndexPath {
    cell.imageView.image = flickrPhoto.thumbnail
    return cell
  }
 
  //3
  if flickrPhoto.largeImage != nil {
    cell.imageView.image = flickrPhoto.largeImage
    return cell
  }
 
  //4
  cell.imageView.image = flickrPhoto.thumbnail
  cell.activityIndicator.startAnimating()
 
  //5
  flickrPhoto.loadLargeImage {
    loadedFlickrPhoto, error in
 
    //6
    cell.activityIndicator.stopAnimating()
 
    //7
    if error != nil {
      return
    }
 
    if loadedFlickrPhoto.largeImage == nil {
      return
    }
 
    //8
    if indexPath == self.largePhotoIndexPath {
      if let cell = collectionView.cellForItemAtIndexPath(indexPath) as? FlickrPhotoCell {
        cell.imageView.image = loadedFlickrPhoto.largeImage
      }
    }
  }
 
  return cell
}

下面为以上代码的释义:

  1. 总是停止activity spinner - 你需要复用cell在图片下载之前.
  2. 这部分和之前相同 - 如果你没有查看大图片,仅设置返回thumbnail即可.
  3. 如果大图片已经加载完,返回它
  4. 当你想要获得大图片,但还没有时,设置spinner运转并通过thumbnail image获取图片,当下载完后缩略图将会被方法.
  5. 异步请求大图片并通过block来返回结果.
  6. 加载完成后,停止spinner.
  7. 如果发生错误或没有图片加载,将不做什么.
  8. 检查放大图片的index path在下载时是否变化,获得正确的cell的index path使图片放大.

运行程序,进行搜索,点击你喜欢的图片,它将填满屏幕,其它的cell将为它腾出足够的空间.

UICollectionView Tutorial Part 2:Reusable Views and Cell Selection_第5张图片
largeimages-666x500.png

再次点击cell,或者滑动后点击其它cell.无需写任何代码,collection view会完成布局变化的动画.

Girl学iOS100天 第14天

你可能感兴趣的:(UICollectionView Tutorial Part 2:Reusable Views and Cell Selection)