我的问题与我尝试使用滚动条进行的自定义分页形式有关,如果您首先考虑在老虎机中实现的滚动视图类型,这更容易可视化。
所以说我的UIScrollView的宽度为100像素。假设它包含3个内部视图,每个视图的宽度为30个像素,使得它们以3个像素的宽度分隔。我想要实现的分页类型是每个页面是我的一个视图(30个像素),而不是滚动视图的整个宽度。
我知道通常,如果视图占用了滚动视图的整个宽度,并且启用了分页,那么一切正常。但是,在我的自定义分页中,我还希望滚动视图中的周围视图也可见。
我该怎么做?
答案 0 :(得分:26)
我刚刚为另一个项目做了这个。您需要做的是将UIScrollView放入UIView的自定义实现中。我为此创建了一个名为ExtendedHitAreaViewController的类。 ExtendedHitAreaView重写hitTest函数以返回它的第一个子对象,它将是你的滚动视图。
您的滚动视图应该是您想要的页面大小,即30px with clipsToBounds = NO。 扩展命中区域视图应该是您想要显示的区域的完整大小,其中clipsToBounds = YES。
将滚动视图作为子视图添加到扩展命中区域视图,然后将扩展命中区域视图添加到viewcontroller的视图中。
@implementation ExtendedHitAreaViewContainer
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if ([self pointInside:point withEvent:event]) {
if ([[self subviews] count] > 0) {
//force return of first child, if exists
return [[self subviews] objectAtIndex:0];
} else {
return self;
}
}
return nil;
}
@end
答案 1 :(得分:12)
从iOS 5开始,有这种委托方法:- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
。
所以你可以这样做:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint
*)targetContentOffset {
if (scrollView == self.scrollView) {
CGFloat x = targetContentOffset->x;
x = roundf(x / 30.0f) * 30.0f;
targetContentOffset->x = x;
}
}
对于更高的速度,如果你想要更加敏捷的感觉,你可能想要调整一下targetContentOffset。
答案 2 :(得分:2)
我遇到了同样的问题,这对我很有用,在iOS 8上测试过。
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset
{
NSInteger index = lrint(targetContentOffset->x/_pageWidth);
NSInteger currentPage = lrint(scrollView.contentOffset.x/_pageWidth);
if(index == currentPage) {
if(velocity.x > 0)
index++;
else if(velocity.x < 0)
index--;
}
targetContentOffset->x = index * _pageWidth;
}
如果速度不为零,我必须检查速度并始终转到下一页/上一页,否则在进行非常短而快速的滑动时会产生非动画跳跃。
更新:这似乎工作得更好:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset
{
CGFloat index = targetContentOffset->x/channelScrollWidth;
if(velocity.x > 0)
index = ceil(index);
else if(velocity.x < 0)
index = floor(index);
else
index = round(index);
targetContentOffset->x = index * channelScrollWidth;
}
这些用于横向滚动视图,使用y
而不是x
作为垂直滚动视图。
答案 3 :(得分:1)
我在滚动视图中有许多不同的视图,包括按钮和手势识别器。
@ picciano的答案不起作用(滚动效果很好,但按钮和识别器没有触及)对我来说,所以我找到了这个解决方案:
class ExtendedHitAreaView : UIScrollView {
// Your insets
var hitAreaEdgeInset = UIEdgeInsets(top: 0, left: -20, bottom: 0, right: -20)
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
let hitBounds = UIEdgeInsetsInsetRect(bounds, hitAreaEdgeInset)
return CGRectContainsPoint(hitBounds, point)
}
}
答案 4 :(得分:1)
我一直在努力克服这个问题,并且找到了一个几乎完美的解决方案,该解决方案非常理想,可以满足您想要的页面宽度。
我将UIScrollView中的 scrollView.isPaging 设置为false(同时默认情况下为false),并将其委托设置为 UIScrollViewDelegate 。
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
// Stop scrollView sliding:
targetContentOffset.pointee = scrollView.contentOffset
if scrollView == scrollView {
let maxIndex = slides.count - 1
let targetX: CGFloat = scrollView.contentOffset.x + velocity.x * 60.0
var targetIndex = Int(round(Double(targetX / (pageWidth + spacingWidth))))
var additionalWidth: CGFloat = 0
var isOverScrolled = false
if targetIndex <= 0 {
targetIndex = 0
} else {
// in case you want to make page to center of View
// by substract width with this additionalWidth
additionalWidth = 20
}
if targetIndex > maxIndex {
targetIndex = maxIndex
isOverScrolled = true
}
let velocityX = velocity.x
var newOffset = CGPoint(x: (CGFloat(targetIndex) * (self.pageWidth + self.spacingWidth)) - additionalWidth, y: 0)
if velocityX == 0 {
// when velocityX is 0, the jumping animation will occured
// if we don't set targetContentOffset.pointee to new offset
if !isOverScrolled && targetIndex == maxIndex {
newOffset.x = scrollView.contentSize.width - scrollView.frame.width
}
targetContentOffset.pointee = newOffset
}
// Damping equal 1 => no oscillations => decay animation:
UIView.animate(
withDuration: 0.3, delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: velocityX,
options: .allowUserInteraction,
animations: {
scrollView.contentOffset = newOffset
scrollView.layoutIfNeeded()
}, completion: nil)
}
}
幻灯片包含您插入到UIScrollView中的所有页面视图。
答案 5 :(得分:0)
经过几天的研究和排查,我想出了一些对我有用的东西!
首先,您需要子类化scrollview所在的视图,并使用以下方法覆盖此方法:
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event {
UIView* child = nil;
if ((child = [super hitTest:point withEvent:event]) == self)
return (UIView *)_calloutCell;
return child;
}
然后所有的魔法都发生在scrollview委托方法
中 -(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
//_lastOffset is declared in the header file
//@property (nonatomic) CGPoint lastOffset;
_lastOffset = scrollView.contentOffset;
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
CGPoint currentOffset = scrollView.contentOffset;
CGPoint newOffset = CGPointZero;
if (_lastOffset.x < currentOffset.x) {
// right to left
newOffset.x = _lastOffset.x + 298;
}
else {
// left to right
newOffset.x = _lastOffset.x - 298;
}
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelay:0.0f];
targetContentOffset->x = newOffset.x;
[UIView commitAnimations];
}
您还需要设置scrollview的减速率。我在ViewDidLoad
中做到了 [self.scrollView setDecelerationRate:UIScrollViewDecelerationRateFast];
答案 6 :(得分:-1)
alcides'解决方案完美无缺。每当我进入scrollviewDidEndDragging和scrollViewWillEndDragging时,我只启用/禁用滚动视图的滚动。如果用户在分页动画完成之前滚动了几次,则单元格略微不对齐。
所以我有:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
scrollView.scrollEnabled = NO
CGPoint currentOffset = scrollView.contentOffset;
CGPoint newOffset = CGPointZero;
if (_lastOffset.x < currentOffset.x) {
// right to left
newOffset.x = _lastOffset.x + 298;
}
else {
// left to right
newOffset.x = _lastOffset.x - 298;
}
[UIView animateWithDuration:0.4 animations:^{
targetContentOffset.x = newOffset.x
}];
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView {
scrollView.scrollEnabled = YES
}