【译】UICollectionView 轻松重排

VernitaDemp 8年前
   <blockquote>     <p>原文链接:<a href="/misc/goto?guid=4958962597439846144" rel="external">UICollectionViews Now Have Easy Reordering</a></p>     <p>原本打算总结一下 UICollectionView 的一些用法,看到一篇比较好的文章,所以直接翻译了。翻译得比较生硬,见谅。</p>    </blockquote>    <p>我超喜欢<code>UICollectionView</code>。相比<code>UITableView</code>,它容易自定义得多。现在我使用甚至使用 collection view 比使用 table view 还要频繁了。在 iOS9 中,它开始支持使用起来很简单的重排。在之前是不可能直接重排的,而且实现起来很麻烦。让我们一起来看看 API。你可以在 <a href="/misc/goto?guid=4958968159462699436" rel="external">Github</a> 上找到对应的 Xcode 项目。</p>    <p>最简单的实现重排是通过使用<code>UICollectionViewController</code>。它现在有一个新的属性叫做<code>installsStandardGestureForInteractiveMovement</code>,作用是添加手势(gestures)来重排 cells。这个属性默认值为<code>True</code>,这意味着要使用它我们只需要重写一个方法。</p>    <table>     <tbody>      <tr>       <td> <pre>  <code class="language-objectivec">func collectionView(collectionView: UICollectionView,      moveItemAtIndexPath sourceIndexPath: NSIndexPath,      toIndexPath destinationIndexPath: NSIndexPath) {      // move your data order      // 可以留空  }  </code></pre> </td>      </tr>     </tbody>    </table>    <p> </p>    <p>当前的 collection view 判定 items 可以被移动,因为<code>moveItemAtIndexPath</code>被重写了。</p>    <p><img alt="【译】UICollectionView 轻松重排" src="https://simg.open-open.com/show/eff1e11a984cdbbaee2e2cd347d410bc.gif" width="372" height="665"></p>    <p>当我们希望在一个简单的<code>UIViewController</code>中使用 collection view 时,会麻烦一点。我们也要实现之前提到的<code>UICollectionViewDataSource</code>方法,不过我们需要重写<code>installsStandardGestureForInteractiveMovement</code>。不用担心,也很简单。<code>UILongPressGestureRecognizer</code>是一种持续性的手势识别器并且完全支持拖动。</p>    <table>     <tbody>      <tr>       <td> <pre>  <code class="language-objectivec">override func viewDidLoad() {      super.viewDidLoad()                longPressGesture = UILongPressGestureRecognizer(target: self, action: "handleLongGesture:")          self.collectionView.addGestureRecognizer(longPressGesture)  }        func handleLongGesture(gesture: UILongPressGestureRecognizer) {            switch(gesture.state) {            case UIGestureRecognizerState.Began:              guard let selectedIndexPath = self.collectionView.indexPathForItemAtPoint(gesture.locationInView(self.collectionView)) else {                  break              }              collectionView.beginInteractiveMovementForItemAtIndexPath(selectedIndexPath)          case UIGestureRecognizerState.Changed:              collectionView.updateInteractiveMovementTargetPosition(gesture.locationInView(gesture.view!))          case UIGestureRecognizerState.Ended:              collectionView.endInteractiveMovement()          default:              collectionView.cancelInteractiveMovement()          }      }  </code></pre> </td>      </tr>     </tbody>    </table>    <p> </p>    <p>我们保存了在 long press gesture 中不活的被选中的 index path 并且基于它是否有值决定允不允许拖动手势生效。然后,我们根据手势状态调用一些新的 collection view 方法。</p>    <ul>     <li><code>beginInteractiveMovementForItemAtIndexPath(indexPath: NSIndexPath)</code>:开始指定位置 cell 的交互移动。</li>     <li><code>updateInteractiveMovementTargetPosition(targetPosition: CGPoint)</code>:更新交互移动对象的位置</li>     <li><code>endInteractiveMovement()</code>:在你结束拖动手势之后结束交互移动</li>     <li><code>cancelInteractiveMovement()</code>:取消交互移动</li>    </ul>    <p>这些让搞定拖动手势非常容易。</p>    <p><img alt="【译】UICollectionView 轻松重排" src="https://simg.open-open.com/show/6e1e70e147d16bace1f555eafb12d8e6.gif" width="372" height="665"></p>    <p>效果和标准的<code>UICollectionViewController</code>一样。很酷对吧,不过更酷的是我们可以将我们自定义的 collection view layout 应用到重排中去。看看下面在简单的瀑布视图中的交互移动。</p>    <p><img alt="【译】UICollectionView 轻松重排" src="https://simg.open-open.com/show/24d14998ff3d42cf685127681b7321ff.gif" width="372" height="665"></p>    <p>嗯,看起来不错,不过如果我们不想在移动的时候改变 cell 大小呢?选中的 cell 大小应该在交互移动时保持一致。这是可以实现的。<code>UICollectionViewLayout</code>也有一些其他的方法来负责重排。</p>    <table>     <tbody>      <tr>       <td> <pre>  <code class="language-objectivec">func invalidationContextForInteractivelyMovingItems(targetIndexPaths: [NSIndexPath],      withTargetPosition targetPosition: CGPoint,      previousIndexPaths: [NSIndexPath],      previousPosition: CGPoint) -> UICollectionViewLayoutInvalidationContext    func invalidationContextForEndingInteractiveMovementOfItemsToFinalIndexPaths(indexPaths: [NSIndexPath],      previousIndexPaths: [NSIndexPath],      movementCancelled: Bool) -> UICollectionViewLayoutInvalidationContext  </code></pre> </td>      </tr>     </tbody>    </table>    <p> </p>    <p>前一个在目标 indexPath 和之前的 indexPath 之间进行移动时调用。另一个类似,不过是在移动结束之后调用。有了这些我们就可以通过一些小手段达到我们的要求。</p>    <table>     <tbody>      <tr>       <td> <pre>  <code class="language-objectivec">internal override func invalidationContextForInteractivelyMovingItems(targetIndexPaths: [NSIndexPath],      withTargetPosition targetPosition: CGPoint,      previousIndexPaths: [NSIndexPath],      previousPosition: CGPoint) -> UICollectionViewLayoutInvalidationContext {        var context = super.invalidationContextForInteractivelyMovingItems(targetIndexPaths,          withTargetPosition: targetPosition, previousIndexPaths: previousIndexPaths,          previousPosition: previousPosition)        self.delegate?.collectionView!(self.collectionView!, moveItemAtIndexPath: previousIndexPaths[0],          toIndexPath: targetIndexPaths[0])        return context  }  </code></pre> </td>      </tr>     </tbody>    </table>    <p> </p>    <p>解决方案非常清晰。获取正在移动的 cell 之前和目标 index path。然后调用<code>UICollectionViewDataSource</code>来移动这些 item。</p>    <p><img alt="【译】UICollectionView 轻松重排" src="https://simg.open-open.com/show/0b2a3226cdb443a70336c4aff42768bf.gif" width="372" height="665"></p>    <p>不用怀疑,collection view 重排是一个非常棒的更新。UIKit 工程师干得太棒了!:)</p>    <p>本文原链: <a href="/misc/goto?guid=4959670502483954522" rel="nofollow,noindex">【译】UICollectionView 轻松重排</a></p>    <p>原文链接: <a href="/misc/goto?guid=4958962597439846144" rel="nofollow,noindex">UICollectionViews Now Have Easy Reordering</a></p>