印象笔记交互效果的实现
啰嗦几句
起初我是想把这个demo做的和原版一摸一样,但在印象笔记的ipa中并没有找到那些关键的图片,网上也没有相关的素材,而且当我在百度搜索里面赫然敲下印象笔记四个大字的时候,百度搜索竟然给我推荐印象笔记面临倒闭这样的关键字!好吧,在当下新技术大潮一波接一波的年代,这种被面临倒闭的公司还有很多,但我个人感情上希望那一天能来的晚一些,反倒是些世界500强,民族企业之类的,我由衷的希望他明天就关门。我也斗胆尝试过用sketch画出所有的切图,但当我画完一个️后就彻底放弃了这个想法,索性也就弄成现在这个样子。
不夸张的说我半路转来做ios和印象笔记这两个交互效果是有很大关系的,在没有作出这个demo之前,我对evernote的这两个交互效果特别的着迷,而在我转做ios之后,这种感觉反而更加强烈了,因为凭我当时那点浅薄的ios经验,完全不知道这个效果是怎么做到的,我也像疯子一样在各种code网站疯狂扫荡一遍也没有找到相应的demo,趁着这段时间比较闲就把它实现了。
弹簧效果:
弹簧.gif使用collectionView,自定义布局继承UICollectionViewFlowLayout,想要这些cell按照我们的意思动起来,首先重写shouldInvalidateLayoutFor
BoundsChange返回true。
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
return true
}
这个动画只在collectionview滑动到顶部和底部会触发,重写layoutAttributesForElementsInRect这个方法根据collectionview的contentoffset计算出cell的frame,这样就ok了。
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let offsetY = self.collectionView!.contentOffset.y
let attrsArray = super.layoutAttributesForElementsInRect(rect)
let collectionViewFrameHeight = self.collectionView!.frame.size.height;
let collectionViewContentHeight = self.collectionView!.contentSize.height;
let ScrollViewContentInsetBottom = self.collectionView!.contentInset.bottom;
let bottomOffset = offsetY + collectionViewFrameHeight - collectionViewContentHeight - ScrollViewContentInsetBottom
let numOfItems = self.collectionView!.numberOfSections()
for attr:UICollectionViewLayoutAttributes in attrsArray! {
if (attr.representedElementCategory == UICollectionElementCategory.Cell) {
var cellRect = attr.frame;
if offsetY <= 0 {
let distance = fabs(offsetY) / SpringFactor;
cellRect.origin.y += offsetY + distance * CGFloat(attr.indexPath.section + 1);
}else if bottomOffset > 0 {
let distance = bottomOffset / SpringFactor;
cellRect.origin.y += bottomOffset - distance *
CGFloat(numOfItems - attr.indexPath.section)
}
attr.frame = cellRect;
}
}
return attrsArray;
}
转场效果:
转场效果.gif自定义类EvernoteTransition
遵守UIViewControllerAnimated Transitioning
和UIViewControllerTransitioningDelegate
,
该类的对象作为transitioningDelegate
。实现
UIViewControllerAnimatedTransitioning
中
的transitionDuration
和animateTransition
方法前者返回动画的时间,后者用来实现转场时的具体动画。
在UIViewControllerTransitioningDelegate
的present和dismiss代理方法中返回EvernoteTransition对象,这样就ok了
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
self.isPresent = true
return self
}
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
self.isPresent = false
return self
}
手势互动:
手势互动.gif实现UIViewControllerTransitioningDelegate中
的interactionControllerForDismissal
func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
self.isPresent = false
return interactionController
}
给第二个controller加上一个UIScreenEdgePanGestureRecognizer手势方向为left,实时的更新interactionController,这样就ok了.
func handlePanGesture(recognizer:UIScreenEdgePanGestureRecognizer) {
let view = panViewController.view
if recognizer.state == UIGestureRecognizerState.Began {
panViewController.dismissViewControllerAnimated(true, completion: { () -> Void in
})
} else if recognizer.state == UIGestureRecognizerState.Changed {
let translation = recognizer.translationInView(view)
let d = fabs(translation.x / CGRectGetWidth(view.bounds))
interactionController.updateInteractiveTransition(d)
} else if recognizer.state == UIGestureRecognizerState.Ended {
if recognizer.velocityInView(view).x > 0 {
interactionController.finishInteractiveTransition()
} else {
interactionController.cancelInteractiveTransition()
listViewController.presentViewController(panViewController, animated: false, completion: { () -> Void in
})
}
interactionController = UIPercentDrivenInteractiveTransition()
}
}
具体源码:
注:在我自认为没有任何商业意图的前提下,从dribbble上面搬来了第一张GIF,如果这篇文章有幸被原制作者看到并且认为我这种做法给他带来了困扰,我便立即删除,最后送大家一首克罗地亚狂想曲。