有关网站建设的外文参考文献,评论 wordpress,谷歌建站,网络规划设计师是副高引言 最近一直在做数据通信相关的工作#xff0c;导致了UI上的一些bug一直没有解决。这两天终于能腾出点时间大概看了一下Redmine上的bug#xff0c;发现有很多bug都是与系统滚动条有关系的。所以索性就关注一下这个小小的滚动条。 为什么要自定义ScrollIndictor 原有的Scrol…引言
最近一直在做数据通信相关的工作导致了UI上的一些bug一直没有解决。这两天终于能腾出点时间大概看了一下Redmine上的bug发现有很多bug都是与系统滚动条有关系的。所以索性就关注一下这个小小的滚动条。 为什么要自定义ScrollIndictor
原有的ScrollIndictor当然是系统提供的滚动条但是为什么会有bug出现呢。这和现有的的需求有关系需求定义是当现有页面内容超过一屏高度的时候滚动条需要常显示不能消失。小于一屏就不需要显示了。这和系统滚动条的显示行为不太一致。所以起初我们单纯的考虑直接修改系统滚动条让他常显示不就OK了。但是经过几轮测试过后发现系统定义成让其消失是为了弥补滚动条时常时短的bug。系统控件存在问题怎么办洪荒之力自定义吧。 现有滚动条显示方案
在谈自定义方案之前还想和大家分享下现有的解决方法让系统滚动条常显。这个方法可谓是剑走偏锋不过思路还是蛮新奇的。具体思路如下 1.定义UIImageView的分类 2.重写setAlpha方法从新定义UIImagView的隐藏行为如果有tag值符合的View令其隐藏行为失效。 3.设置TableViewCollectionView ScrollVIew的Tag值等于 noDisableVerticalScrollTag 或者 noDisableHorizontalScrollTag。 因为ScrollVIew的滚动条就是一个UIImageView但是我们不能拿到这个滚动条的实例和隐藏掉用时机的。但是我们可以重新定义UIImageView的行为方法控制起显示和隐藏的过程。具体代码如下。 #define noDisableVerticalScrollTag 836913 #define noDisableHorizontalScrollTag 836914 #import UIImageViewForScrollView.h implementation UIImageView (ForScrollView) - ( void) setAlpha:( CGFloat)alpha { if ( self. superview. tag noDisableVerticalScrollTag) { if (alpha 0 self. autoresizingMask UIViewAutoresizingFlexibleLeftMargin) { if ( self. frame. size. width 10 self. frame. size. height self. frame. size. width) { UIScrollView *sc ( UIScrollView*) self. superview; if (sc. frame. size. height sc. contentSize. height) { return; } } } } if ( self. superview. tag noDisableHorizontalScrollTag) { if (alpha 0 self. autoresizingMask UIViewAutoresizingFlexibleTopMargin) { if ( self. frame. size. height 10 self. frame. size. height self. frame. size. width) { UIScrollView *sc ( UIScrollView*) self. superview; if (sc. frame. size. width sc. contentSize. width) { return; } } } } [ super setAlpha:alpha]; } end 现有方案存在的问题
上述的方案堪称是一劳永逸了其他地方不需要修改任何代码就可以达到需求。但是测试时发现在ScrollView滚动到底部的时候滚动条会突然变长。而且快速改变滚动方向的时候滚动条会出现闪烁的效果。这都会影响用户体验必须要修改掉这样的问题。 开始自定义之旅
由于系统中用的最多的就是CollectionView所以就先从这个CollecitonView的ScrollIncidtor开始吧。自定义可以采用分类也可以采用基类的形式。都各有利弊。采用分类在可以避免与工程中其他的类出现耦合的情况代码更加集中好管理。但是不能重写父类的方法例如reloadData。如果强行重写的话可能导致系统时序的混乱。所以我这里才用的是基类的方案。 其中比较难理解的就是滚动条在滑动到顶端和底端的时候都要有个弹力的效果。这个效果的计算方法当时想的比较复杂。但是最后实现的时候发现也不是很难。具体的说明参见注释代码如下。 #import BaseCollectionView.h #import Constants.h implementation BaseCollectionView{ UIView *scrollIndicatorView; CGFloat contentSizeHeight; } - ( void)drawRect:( CGRect)rect { [ self enableCustomVerticalScrollIndicatorWithColor:[ UIColor lightGrayColor]]; } - ( void)reloadData{ [ super reloadData]; [ self setNeedsDisplay]; } - ( UIView *)createIndicatorViewWithFrame:( CGRect) frame{ UIView *indicator [[ UIView alloc] initWithFrame:frame]; indicator. layer. cornerRadius ScrollIndicatorWidth/ 2.0f; // viewScrollIndicator.alpha 0.0f; // viewScrollIndicator.layer.borderWidth 1.0f; // viewScrollIndicator.layer.borderColor indicatorColor.CGColor; [ self addSubview:indicator]; return indicator; } //Calculate the real height of scroll indictor accroding to the content size. - ( CGFloat)getNormalScrollIndictorHeight{ CGFloat percent self. frame. size. height / self. contentSize. height; float normalHeight MAX( 0.0f,(percent * self.frame.size.height)); return normalHeight; } - ( void)enableCustomVerticalScrollIndicatorWithColor:( UIColor *)indicatorColor { self. showsVerticalScrollIndicator NO; float height [ self getNormalScrollIndictorHeight]; CGRect frame CGRectMake( self. frame. size. width - ScrollIndicatorWidth - ScrollIndicatorRightSpace, 0.0f, ScrollIndicatorWidth, height); if( scrollIndicatorView nil){ scrollIndicatorView [ self createIndicatorViewWithFrame:frame]; [ self addKVOObservers]; } else{ scrollIndicatorView. frame frame; } [ scrollIndicatorView setBackgroundColor:[indicatorColor colorWithAlphaComponent: 0.75]]; //If content size is larger than frame size, the indictor will be displayed. if( self. frame. size. height self. contentSize. height){ scrollIndicatorView. alpha 0; } else{ scrollIndicatorView. alpha 1.0; } [ self refreshVerticalScrollIndicator]; } - ( void)refreshVerticalScrollIndicator { if ( self. contentSize. height 0) { return; } //Get the current frame of scroll indicator CGRect rect scrollIndicatorView. frame; //Get the normal height of Indicator float normalHeight [ self getNormalScrollIndictorHeight]; //Calculate the real content offset ratio. CGFloat maxConentOffset self. contentSize. height - self. frame. size. height; CGFloat offsetRatio self. contentOffset. y / maxConentOffset; //Calculate the indictor offset CGFloat maxIndicatorOffset ( self. frame. size. height - normalHeight); CGFloat indicatorOffset offsetRatio * maxIndicatorOffset; //if scrolling out of top limitation, the scroll indictor will be compressed. if (indicatorOffset 0) { rect. size. height normalHeight indicatorOffset; } //if scrolling out of bottom limitation, the scroll indictor will be compressed again. else if(indicatorOffset self. frame. size. height - normalHeight){ rect. size. height normalHeight- (indicatorOffset - maxIndicatorOffset); // indicatorOffset self.frame.size.height - normalHeight; } else{ rect. size. height normalHeight; } rect. origin. y self. contentOffset. y MAX( 0.0f,indicatorOffset); if (rect. size. height ScrollIndicatorMinHeight) { rect. size. height ScrollIndicatorMinHeight; } scrollIndicatorView. frame rect; } - ( void)dealloc { [ self removeKVOObservers]; } #pragma mark - KVO - ( void)addKVOObservers { [ self addObserver: self forKeyPath: contentSize options:( NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context: NULL]; [ self addObserver: self forKeyPath: contentOffset options:( NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context: NULL]; } - ( void)removeKVOObservers { [ self removeObserver: self forKeyPath: contentSize]; [ self removeObserver: self forKeyPath: contentOffset]; } - ( void) observeValueForKeyPath: ( NSString *) keyPath ofObject: ( id) object change: ( NSDictionary *) change context: ( void *) context { if ( self. contentSize. width 0.0f) { [ self refreshVerticalScrollIndicator]; /* UIView *viewScrollIndicator [self getViewForHorizontalScrollIndicator]; CGRect rect self.frame; CGFloat pourcent self.contentOffset.x / self.contentSize.width; viewScrollIndicator.hidden self.contentSize.width self.frame.size.width; rect.size.width self.frame.size.width * (self.frame.size.width / self.contentSize.width); rect.origin.x pourcent * self.frame.size.width; viewScrollIndicator.frame rect; */ } } end 有待完善和改进的地方
由于当时写的时候没有考虑有很多类型的控件都有这样的需求例如CollectionViewTableViewTextView等等带滚动条的控件。所以导致需要创建大量的类以应对同的类型的需要。以后的改进方向就是将滚动条的主控逻辑抽象到ScrollVIew里面去从而减少重复代码和减少类的数量。