Git Product home page Git Product logo

ltinfinitescrollview's Introduction

LTInfiniteScrollView

Demo

1. You can apply animation to each view during the scroll:

LTInfiniteScrollView

2. The iOS 9 task switcher animation can be implemented in ten minutes with the support of this lib:

LTInfiniteScrollView

3. The fancy menu can also be implemented easily:

LTInfiniteScrollView

4. Vertical scroll is also supported:

LTInfiniteScrollView

Usage

Create the scroll view by:

self.scrollView = [[LTInfiniteScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), 200)];
[self.view addSubview:self.scrollView];
self.scrollView.dataSource = self;
[self.scrollView reloadDataWithInitialIndex: 0];

Then implement LTInfiniteScrollViewDataSource protocol:

@protocol LTInfiniteScrollViewDataSource<NSObject>
- (UIView *)viewAtIndex:(NSInteger)index reusingView:(UIView *)view;
- (NSInteger)numberOfViews;
- (NSInteger)numberOfVisibleViews;
@end

Sample code:

- (NSInteger)numberOfViews
{
    // you can set it to a very big number to mimic the infinite behavior, no performance issue here
    return 9999; 
}

- (NSInteger)numberOfVisibleViews
{
    return 5;
}

- (UIView *)viewAtIndex:(NSInteger)index reusingView:(UIView *)view;
{
    if (view) {
        ((UILabel *)view).text = [NSString stringWithFormat:@"%ld", index];
        return view;
    }
    
    UILabel *aView = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 64, 64)];
    aView.backgroundColor = [UIColor blackColor];
    aView.layer.cornerRadius = 32;
    aView.layer.masksToBounds = YES;
    aView.backgroundColor = [UIColor colorWithRed:0/255.0 green:175/255.0 blue:240/255.0 alpha:1];
    aView.textColor = [UIColor whiteColor];
    aView.textAlignment = NSTextAlignmentCenter;
    aView.text = [NSString stringWithFormat:@"%ld", index];
    return aView;
}

LTInfiniteScrollView interface:

@interface LTInfiniteScrollView: UIView
@property (nonatomic, readonly) NSInteger currentIndex;
@property (nonatomic, weak) id<LTInfiniteScrollViewDataSource> dataSource;
@property (nonatomic, weak) id<LTInfiniteScrollViewDelegate> delegate;
@property (nonatomic) BOOL verticalScroll;
@property (nonatomic) BOOL scrollEnabled;
@property (nonatomic) BOOL pagingEnabled;
@property (nonatomic) BOOL bounces;
@property (nonatomic) UIEdgeInsets contentInset;
@property (nonatomic) NSInteger maxScrollDistance;

- (void)reloadDataWithInitialIndex:(NSInteger)initialIndex;
- (void)scrollToIndex:(NSInteger)index animated:(BOOL)animated;
- (UIView *)viewAtIndex:(NSInteger)index;
- (NSArray *)allViews;
@end

If you want to apply any animation during scrolling, implement LTInfiniteScrollViewDelegate protocol:

@protocol LTInfiniteScrollViewDelegate<NSObject>
- (void)updateView:(UIView *)view withProgress:(CGFloat)progress scrollDirection:(ScrollDirection)direction;
@end

The value of progress indicates the relative position of that view, if there are 5 visible views, the value will be ranged from -2 to 2:

|                  |
|-2  -1   0   1   2|
|                  |

You can clone the project and investigate the example for details.

the Swift version

Here's the Swift version: https://github.com/ltebean/LTInfiniteScrollView-Swift

ltinfinitescrollview's People

Contributors

imokhles avatar ltebean avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ltinfinitescrollview's Issues

New Type For Scroll At Index 0

Here I modify your code, and now it can scroll at index 0, and it is convenient to load NSDate Data,that like app which is named "Glow".

//
//  LTInfiniteScrollView.m
//  LTInfiniteScrollView
//
//  Created by ltebean on 14/11/21.
//  Copyright (c) 2014年 ltebean. All rights reserved.
//



#import "LTInfiniteScrollView.h"

#ifndef kScrollViewDefaultType
#define kScrollViewDefaultType         0
#endif

@interface LTInfiniteScrollView()<UIScrollViewDelegate>
@property (nonatomic) CGSize viewSize;
@property (nonatomic,strong) UIScrollView *scrollView;
@property (nonatomic,strong) NSMutableArray *views;
@property (nonatomic) NSInteger visibleViewCount;
@property (nonatomic) NSInteger totalViewCount;
@property (nonatomic) CGFloat preContentOffsetX;
@property (nonatomic) CGFloat totalWidth;
@property (nonatomic) BOOL dragging;
@property (nonatomic) BOOL initialized;
@property (nonatomic) ScrollDirection scrollDirection;
@end

@implementation LTInfiniteScrollView

- (void)setup
{
    self.scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds))];
    self.scrollView.showsHorizontalScrollIndicator = NO;
    self.scrollView.delegate = self;
    self.scrollView.clipsToBounds = NO;
    self.scrollView.pagingEnabled = self.pagingEnabled;
    [self addSubview:self.scrollView];

    self.views = [NSMutableArray array];
}

#pragma mark - public methods

- (void)setPagingEnabled:(BOOL)pagingEnabled
{
    _pagingEnabled = pagingEnabled;
    self.scrollView.pagingEnabled = pagingEnabled;
}

- (void)setScrollEnabled:(BOOL)scrollEnabled
{
    _scrollEnabled = scrollEnabled;
    self.scrollView.scrollEnabled = scrollEnabled;
}

- (void)reloadData
{
    if (!self.initialized) {
        [self setup];
        self.initialized = YES;
    }

    for (UIView *view in self.views) {
        [view removeFromSuperview];
    }
    [self.views removeAllObjects];

    self.visibleViewCount = [self.dataSource numberOfVisibleViews];
    self.totalViewCount = [self.dataSource numberOfViews];

    CGFloat viewWidth = CGRectGetWidth(self.bounds)/self.visibleViewCount;
    CGFloat viewHeight = CGRectGetHeight(self.bounds);
    self.viewSize = CGSizeMake(viewWidth, viewHeight);

    self.totalWidth = viewWidth * self.totalViewCount;

    self.scrollView.contentSize = CGSizeMake(self.totalWidth, CGRectGetHeight(self.bounds));
#if kScrollViewDefaultType
    self.scrollView.contentOffset = CGPointMake(self.totalWidth / 2-CGRectGetWidth(self.bounds) / 2, 0);
#else
    [self.scrollView setContentOffset:CGPointMake(0, 0)];
#endif
    int begin = -ceil(self.visibleViewCount / 2.0f);
    int end = ceil(self.visibleViewCount / 2.0f);

    self.currentIndex = 0;

    CGFloat currentCenter = [self currentCenter].x;

    for (int i = begin; i<= end;i++) {
        UIView *view = [self.dataSource viewAtIndex:i reusingView:nil];
        view.center = [self centerForViewAtIndex:i];
        view.tag = i;
        [self.views addObject:view];
        [self.scrollView addSubview:view];
        [self.delegate updateView:view withDistanceToCenter:(view.center.x - currentCenter) scrollDirection:self.scrollDirection];
    }
}

- (void)scrollToIndex:(NSInteger)index animated:(BOOL)animated
{
    self.dragging = YES;
    [self.scrollView setContentOffset:[self contentOffsetForIndex:index] animated:animated];
}

- (UIView *)viewAtIndex:(NSInteger)index
{
    CGPoint center = [self centerForViewAtIndex:index];
    for (UIView *view in self.views) {
        if (fabs(center.x - view.center.x) <= self.viewSize.width / 2.0f) {
            return view;
        }
    }
    return nil;
}

- (NSArray *)allViews
{
    return self.views;
}

# pragma mark - UIScrollView delegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
#if kScrollViewDefaultType
    CGFloat offset = [self currentCenter].x - self.totalWidth / 2;
    self.currentIndex = ceil((offset- self.viewSize.width / 2) / self.viewSize.width);
#else
    CGFloat offset = self.scrollView.contentOffset.x;
    self.currentIndex = floor(offset / self.viewSize.width);
#endif
    for (UIView *view in self.views) {
        if ([self viewCanBeQueuedForReuse:view]) {
            NSInteger indexNeeded;
            NSInteger indexOfViewToReuse = (NSInteger)view.tag;
            if (indexOfViewToReuse < self.currentIndex) {
                indexNeeded = indexOfViewToReuse + self.visibleViewCount + 2;
            } else {
                indexNeeded = indexOfViewToReuse - (self.visibleViewCount + 2);
            }

            [view removeFromSuperview];

            UIView *viewNeeded = [self.dataSource viewAtIndex:indexNeeded reusingView:view];
            viewNeeded.center = [self centerForViewAtIndex:indexNeeded];
            [self.scrollView addSubview:viewNeeded];
            viewNeeded.tag = indexNeeded;
        }

        CGFloat currentCenter = [self currentCenter].x;
        [self.delegate updateView:view withDistanceToCenter:(view.center.x - currentCenter) scrollDirection:self.scrollDirection];
    }

    if (self.scrollView.contentOffset.x > self.preContentOffsetX) {
        self.scrollDirection = ScrollDirectionLeft;
    } else {
        self.scrollDirection = ScrollDirectionRight;
    }

    self.preContentOffsetX = self.scrollView.contentOffset.x;
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    self.dragging = YES;
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    self.dragging = NO;
    if (!self.pagingEnabled && !decelerate) {
        [self.scrollView setContentOffset:[self contentOffsetForIndex:self.currentIndex] animated:YES];
    }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    if (!self.pagingEnabled) {
        [self.scrollView setContentOffset:[self contentOffsetForIndex:self.currentIndex] animated:YES];
    }
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    if (self.maxScrollDistance <= 0) {
        return;
    }

    CGFloat targetX = targetContentOffset -> x;
    CGFloat currentX = [self contentOffsetForIndex:self.currentIndex].x;

#if kScrollViewDefaultType
    if (fabs(targetX - currentX) <= self.viewSize.width / 2) {
        return;
    } else if {
        NSInteger distance = self.maxScrollDistance - 1;
        NSInteger currentIndex = [self currentIndex];
        NSInteger targetIndex = self.scrollDirection == ScrollDirectionLeft ? currentIndex + distance : currentIndex - distance;
        targetContentOffset -> x = [self contentOffsetForIndex:targetIndex].x;
    }
#else
    if (fabs(targetX - currentX) <= self.viewSize.width / 2) {
        return;
    } else {
        NSInteger radio = (NSInteger)(fabs(targetX - currentX) / self.viewSize.width);
        NSInteger distance = MIN(4, MAX(1, radio));
        NSInteger currentIndex = [self currentIndex];
        NSInteger targetIndex = self.scrollDirection == ScrollDirectionLeft ? currentIndex + distance : currentIndex - distance;
        targetContentOffset -> x = [self contentOffsetForIndex:targetIndex].x;
    }
#endif
}

#pragma mark - helper methods

- (CGPoint)currentCenter
{
    CGFloat x = self.scrollView.contentOffset.x + CGRectGetWidth(self.bounds) / 2.0f;
    CGFloat y = self.scrollView.contentOffset.y;
    return  CGPointMake(x, y);
}

- (CGPoint)contentOffsetForIndex:(NSInteger)index
{
#if kScrollViewDefaultType
    CGFloat x = self.totalWidth / 2 + index*self.viewSize.width - CGRectGetWidth(self.bounds) / 2;
#else
    CGFloat x = index*self.viewSize.width;
#endif
    return CGPointMake(x, 0);
}

- (CGPoint)centerForViewAtIndex:(NSInteger)index
{
    CGFloat y = CGRectGetMidY(self.bounds);
#if kScrollViewDefaultType
    CGFloat x = self.totalWidth/2 + index * self.viewSize.width;
#else
    CGFloat x = index * self.viewSize.width + CGRectGetWidth(self.bounds) / 2.0f;
#endif
    return CGPointMake(x, y);
}

- (BOOL)viewCanBeQueuedForReuse:(UIView *)view
{
    CGFloat distanceToCenter = [self currentCenter].x - view.center.x;

    CGFloat threshold = (ceil(self.visibleViewCount/2.0f)) * self.viewSize.width + self.viewSize.width / 2.0f;
    if (self.scrollDirection == ScrollDirectionLeft) {
        if (distanceToCenter < 0){
            return NO;
        }
    } else {
        if (distanceToCenter > 0) {
            return NO;
        }
    }
    if (fabs(distanceToCenter) > threshold) {
        return YES;
    }
    return NO;
}
@end

Cards visible after going back

For the task switcher, when i go back from task switcher to the main view, the cards are visible for a little bit after the transition

feb 13 2016 00 13

Always center

Hi, is it possible to make the selected index in center like 360 degree? similar to UIPickerView behavior

Can't scroll to the end

I set the number of views to 20 in the task switcher demo and it does not scroll to the end. It bounces back to the 3rd to last view and leaves 2 views behind it.

feb 09 2016 11 59

no infinite

I tried to create a scroll like your example, but never be infinite... go from view 0 to last one and the scroll stop.

Your example has the same error

Running on Device

When running Task Switcher on an actual device, reArrangeViews method triggers an infinite loop.
Device is an iPhone 6s running iOS 9.2.1

How to embed scrollview into prarent view?

When I set the width of the scrollView in DemoViewController, eg.200. I want the scrollView also only can see 5 labels and other labels are hiden. But I can see 7 labels when I scroll and two of them are become out-of-bounds

Something wrong with view reusing

image
when the scrollView's contentOffset.x is large, then scroll to contentOffset.x =0, the reuseView disappear.
Any idea?

for (UIView *view in self.views) {
        if ([self viewCanBeQueuedForReuse:view]) {
            NSLog(@"YES");
            NSInteger indexNeeded;
            NSInteger indexOfViewToReuse = (NSInteger)view.tag;
            if (indexOfViewToReuse < self.currentIndex) {
                indexNeeded = indexOfViewToReuse + self.visibleViewCount + 2;
            } else {
                indexNeeded = indexOfViewToReuse - (self.visibleViewCount + 2);
            }

            [view removeFromSuperview];

            UIView *viewNeeded = [self.dataSource viewAtIndex:indexNeeded reusingView:view];
            viewNeeded.center = [self centerForViewAtIndex:indexNeeded];
            [self.scrollView addSubview:viewNeeded];
            viewNeeded.tag = indexNeeded;
        } else {
            NSLog(@"NO");
        }

        CGFloat currentCenter = [self currentCenter].x;
        [self.delegate updateView:view withDistanceToCenter:(view.center.x - currentCenter) scrollDirection:self.scrollDirection];
    }

it always print YES, while the reuseView disappear.

Looking forward to your reply.

POD

can you plz make it pod

loop is not increment in numberofViews

- (NSInteger)numberOfViews {

    for (int i = 0; i<[masterArray count]; i++)  {
        NSMutableArray *test = masterArray[i];
        return  test.count;
    }
}

in this method loop is not increment, i got zero every time when i using this loop in outside of this scope this is proper work.
plz help me.

Fix Bugs

hi,man.
I modify these codes that makes my project fine.

     //    if (self.dragging) {
        if (self.scrollView.contentOffset.x > self.preContentOffsetX) {
            self.scrollDirection = ScrollDirectionLeft;
        } else {
            self.scrollDirection = ScrollDirectionRight;
        }
    //    }
    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
    {
        self.dragging = NO;
        if (!self.pagingEnabled && !decelerate) {
            [self.scrollView setContentOffset:[self contentOffsetForIndex:self.currentIndex] animated:YES];
        }
    }

Scroll problem when number of Views is even number

Change in the file DemoViewController.m in your demo code like below:

define NUMBER_OF_VISIBLE_VIEWS 1

....

  • (NSInteger)numberOfViews
    {
    return 4;
    }
    It displays incorrect : display 5 views instead 4, and the first and last view just can scroll to a half of its view

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.