I have an app which uses a tableview, and sometimes there can be as many as 100 items in this tableview. Thus, I have been assigned to customize this scroll indicator, so that it is easy for the user to scroll through their contents. To do this, I need to implement the following:
1) I need the scroll indicator to always show.
2) I need to change the scroll indicator from the iOS default gray indicator to an orange one, and add a label in the middle of it which extends inward. This label will have the date of the cell. As you scroll down in the scroll bar, the date changes to reflect where you are on the page. (See image for clarification).
3) When you click and hold this custom TableView's scroll indicator, it enables fast scrolling.
What is the best way to approach this? Should I use a library?
This isn't a perfect solution but this what I came up with and this should get you on the right track to perfect this solution to your needs. Basically there are a few basic steps you need to do:
1 Instantiate a tableView (UITableView) and scrollViewIndicator (UIView)
2 Calculate the height of the scrollIndicator based upon the contentSize of the tableView. Then add both the tableView and scrollIndicator to the container view, and the scroll indicator above the tableView with it's alpha property set to 0 (to fade in one we scroll)
3 Check the contentOffset of the tableView (subclass of UIScrollView) and move the scrollIndicator based upon this value
4 Fade the scrollIndicator out once the tableView has decelerated
You're specific custom scroll indicator are going to determined by your project and needs. This should get you on the right track but I think your biggest issue is going to be calculating the height of the scrollIndicator once "paging" is introduced. But I have faith in you! Good luck my friend.
#import "ViewController.h"
static CGFloat indicatorPadding = 5;
static CGFloat indicatorHeightMultiplyer = 0.05;
static CGFloat indicatorWidth = 3;
static CGFloat indicatorShowAnimation = 0.10;
#interface ViewController () <UITableViewDataSource, UITableViewDelegate> {
CGFloat lastScrollOffset;
BOOL isFadingIndicator;
}
#property (strong, nonatomic) UITableView *tableView;
#property (strong, nonatomic) UIView *scrollIndicator;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
isFadingIndicator = NO;
// Set Up TableView
self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
self.tableView.rowHeight = 100;
self.tableView.dataSource = self;
self.tableView.delegate = self;
self.tableView.showsHorizontalScrollIndicator = NO;
self.tableView.showsVerticalScrollIndicator = NO;
[self.tableView layoutIfNeeded];
// Calculate indicator size based on TableView contentSize
CGFloat indicatorHeight = self.tableView.contentSize.height * indicatorHeightMultiplyer;
// Set Up Scroll Indicator
self.scrollIndicator = [[UIView alloc]initWithFrame:CGRectMake(CGRectGetWidth(self.tableView.frame) - indicatorPadding, indicatorPadding, indicatorWidth, indicatorHeight)];
self.scrollIndicator.backgroundColor = [UIColor orangeColor];
self.scrollIndicator.layer.cornerRadius = self.scrollIndicator.frame.size.width / 2;
// Add TableView
[self.view addSubview:self.tableView];
// Add Scroll Indicator
[self.view addSubview:self.scrollIndicator];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#pragma mark - TABLE VIEW METHODS
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
return cell;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 10;
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.tableView) {
[self showIndicator];
if (lastScrollOffset >= scrollView.contentOffset.y) {
[self moveScrollIndicatorDownward:YES withOffset:scrollView.contentOffset.y];
}
else {
[self moveScrollIndicatorDownward:NO withOffset:scrollView.contentOffset.y]; // upward
}
lastScrollOffset = scrollView.contentOffset.y;
}
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self hideIndicator];
}
#pragma mark - SCROLL INDICATOR METHODS
-(void)showIndicator {
if (self.scrollIndicator.alpha == 0 && isFadingIndicator == NO) {
// fade in scroll indicator
isFadingIndicator = YES;
[UIView animateWithDuration:indicatorShowAnimation animations:^{
self.scrollIndicator.alpha = 1;
} completion:^(BOOL finished) {
isFadingIndicator = NO;
}];
}
}
-(void)hideIndicator {
if (self.scrollIndicator.alpha == 1 && isFadingIndicator == NO) {
// fade out scroll indicator
isFadingIndicator = YES;
[UIView animateWithDuration:indicatorShowAnimation animations:^{
self.scrollIndicator.alpha = 0;
} completion:^(BOOL finished) {
isFadingIndicator = NO;
}];
}
}
-(void)moveScrollIndicatorDownward:(BOOL)downwards withOffset:(CGFloat)offset {
if ([self canMoveScrollIndicator:downwards]) {
self.scrollIndicator.center = CGPointMake(CGRectGetMidX(self.scrollIndicator.frame), (CGRectGetHeight(self.scrollIndicator.frame) / 2) + offset);
}
else {
// maybe 'bounce' scroll indicator if isDecelerting is YES?
}
}
-(BOOL)canMoveScrollIndicator:(BOOL)downwards {
if (downwards) {
if (self.scrollIndicator.frame.origin.y >= self.tableView.frame.size.height - indicatorPadding) {
return NO;
}
else {
return YES;
}
}
else {
// upwards
if ((self.scrollIndicator.frame.origin.y + self.scrollIndicator.frame.size.height) <= self.tableView.frame.origin.y + indicatorPadding) {
return NO;
}
else {
return YES;
}
}
}
#end
You must hidden native scroll indicator: tableView.showsVerticalScrollIndicator = false And create custom UIView object which will be moving when tableView.contentOffset.y changes. it can be traced with the help scrollViewDidScroll function.
Related
I have a menuView in a list view controller. The menuView added on the UITableViewCell when a more button in the cell being taped.
I achieved the effect with singleton.
#implementation ProductsOperationMenu
static ProductsOperationMenu *_instance;
+ (instancetype)sharedInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] initWithFrame:CGRectZero];
});
return _instance;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setup];
}
return self;
}
ZBMyProductsCell.m
#implementation ZBMyProductsCell
- (void)awakeFromNib
{
[super awakeFromNib];
_operationMenu = [[ProductsOperationMenu alloc] initWithFrame: CGRectZero];
}
- (IBAction)operationButtonClick:(UIButton *)sender {
if ([self.contentView.subviews containsObject:_operationMenu]) {
_operationMenu.hidden = ![_operationMenu isHidden];
} else{
[self.contentView addSubview:_operationMenu];
_operationMenu.hidden = NO;
}
[_operationMenu mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(205);
make.height.mas_equalTo(60);
make.bottom.mas_equalTo(self.operationButton).offset(0);
make.right.mas_equalTo(self.operationButton.mas_left).offset(-10);
}];
}
Without Singleton, it became this:
So the question come.
I want to put the menuView on the controller's view, because it is unique or hidden, which used to belong to the cell.
How to convert layout of the more button selected to the controller's view?
How to use the methods to calculate?
- convertPoint:toView:
- convertPoint:fromView:
......
I did it in a simple way. Here is the code:
- (void)clickOperationButtonOfProductsCell:(ZBMyProductsCell *)myProductsCell{
NSUInteger * operationIndex = [self.myProductsTableView.visibleCells indexOfObject:myProductsCell];
CGFloat originY = operationIndex.row * 110 + 50 + 40;
CGRect originFrame = CGRectMake(KScreenWidth - 55, originY, 0, 60);
CGRect finalFrame = CGRectMake(KScreenWidth - 260, originY, 205, 60);
self.operationMenuView.frame = originFrame;
[UIView animateWithDuration: 0.5 delay: 0 options: UIViewAnimationOptionCurveEaseIn animations:^{
self.operationMenuView.frame = finalFrame;
} completion:^(BOOL finished) { }];
}
How to achieve it more adaptively?
Maybe you can try it like this:
// here is your cell where the more button belongs to
#interface ZBMyProductsCell: UITableViewCell
#property (nonatomic, copy) void(^moreAction)(ZBMyProductsCell *cell);
#end
#implementation ZBMyProductsCell
- (void)_moreButtonClicked:(UIButton *)sender {
!self.moreAction ?: self.moreAction(self);
}
#end
// here is your view controller where your table view belongs to
// this is a `UITableViewDataSource` delegate method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ZBMyProductsCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ZBMyProductsCell" forIndexPath:indexPath];
// Configure the cell...
cell.moreAction = ^(ZBMyProductsCell *cell) {
CGRect rect = [tableView rectForRowAtIndexPath:indexPath];
// write your own code to show/hide the menu
};
return cell;
}
Create a variable in each cell model called cellIndexpath and in cellForRow init it
cell.cellIndexpath = indexpath
have a look of UIPopoverPresnetationController and see if it can do the job. It should be available on iPhone. Putting menu view on the controller’viewwill cause issue when you scroll the table view.
use
UIMenuController *menu = [UIMenuController sharedMenuController];
[menu setTargetRect:self.detaiLabel.frame inView:self];
Basically, I wanna create a static UIButton on top whether or not the table view is scrolled. However, I tried to add the UIButton as the subview of the UIView and make it on the top by using "bringSubviewToFront" method. However, the UIButton still moves when I scroll the UITableView. Therefore, how can I make a static UIButton overlaying the UITableView?
Here is my code:
#import "DiscoverTimelineTableViewController.h"
#interface DiscoverTimelineTableViewController ()
#property (strong, nonatomic) UIView* myview;
#end
#implementation DiscoverTimelineTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self displayMakePlanButton];
NSLog(#"%f", self.view.layer.zPosition);
NSLog(#"%f", self.tableView.layer.zPosition);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
#warning Potentially incomplete method implementation.
// Return the number of sections.
return 1;
}
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 100.0f;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
// Configure the cell...
cell.textLabel.text = #"Sleepy";
return cell;
}
#pragma mark - Add the make plan button
- (void) displayMakePlanButton
{
CGFloat buttonWidth = self.view.frame.size.width;
CGFloat buttonHeight = 104.0f;
CGFloat navBarHeight = self.navigationController.navigationBar.frame.size.height;
CGFloat statBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height;
UIButton *makePlanButton = [UIButton buttonWithType: UIButtonTypeCustom];
[makePlanButton setFrame:CGRectMake(0, self.view.frame.size.height - buttonHeight - navBarHeight - statBarHeight, buttonWidth, buttonHeight)];
[makePlanButton setBackgroundColor:[UIColor colorWithRed:0.286 green:0.678 blue:0.231 alpha:1]];
[makePlanButton setTitle:#"MAKE PLAN" forState:UIControlStateNormal];
[self.view addSubview:makePlanButton];
//makePlanButton.layer.zPosition = 1;
[self.view bringSubviewToFront:makePlanButton];
NSLog(#"%f", makePlanButton.layer.zPosition);
}
#end
You're using a table view controller, so self.view is the table view. A table view is a scroll view, so the view scrolls along with everything else.
You should use a regular view controller with a table as a subview instead.
Alternatively, you could try resetting the view's frame in the scrollViewDidScroll: scroll view delegate method, but I think the view would still jitter a bit.
Finally, you could add the button directly to the UIWindow, but that's likely to bring up a whole host of other problems with rotations, animations, and transitions.
I have a UICollectionView that when a cell is pressed, there will be a new set of data and will animate the flow layout.
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
for(int i=0;i<200;i++)
{
[tempArray addObject:[NSNumber numberWithInt:i]];
}
items = [NSArray arrayWithArray:tempArray];
CollapseLayout *currentLayout = (CollapseLayout *)_collectionView.collectionViewLayout;
currentLayout.collapse = YES;
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.collectionView.numberOfSections)]];
} completion:nil];
}
It is using a Custom UICollectionViewFlowLayout that performs an animation when reloading the data.
My CustomFlowLayout Class is here
#import "CollapseLayout.h"
#implementation CollapseLayout
- (void)prepareLayout {
self.itemSize = CGSizeMake(200, 100);
self.minimumInteritemSpacing = 30;
self.scrollDirection = UICollectionViewScrollDirectionVertical;
}
- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray* allAttributesInRect = [super layoutAttributesForElementsInRect:rect];
if(_collapse)
{
for(UICollectionViewLayoutAttributes *attribute in allAttributesInRect)
{
attribute.frame = CGRectMake(attribute.frame.origin.x - self.collectionView.frame.size.width, attribute.frame.origin.y, attribute.frame.size.width, attribute.frame.size.height);
}
}
return allAttributesInRect;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewLayoutAttributes* allAttributesInRect = [super layoutAttributesForItemAtIndexPath:indexPath];
if(_collapse)
{
allAttributesInRect.frame = CGRectMake(allAttributesInRect.frame.origin.x - self.collectionView.frame.size.width, allAttributesInRect.frame.origin.y, allAttributesInRect.frame.size.width, allAttributesInRect.frame.size.height);
}
return allAttributesInRect;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}
- (UICollectionViewLayoutAttributes*)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath*)itemIndexPath
{
UICollectionViewLayoutAttributes* layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:itemIndexPath];
return layoutAttributes;
}
- (void)invalidateLayout
{
}
The BOOL
_collapse
is set is called in my collection view to trigger the animation. I am hoping to have the old cells, move from left to right, outside the view, and the new cells animate in from the right side of the screen. Right now, the cells are just moving off screen.
My issue is that the code in layoutAttributesForItemAtIndexPath and layoutAttributesForElementsInRect are not animating correctly. Would adding the animation code in these methods be the best place to perform the animations, or im totally off?
Thanks
You shouldn't do anything to the result of
[super layoutAttributesForItemAtIndexPath:indexPath];
nor
[super layoutAttributesForElementsInRect:rect];
If you want your cells to animate from where they are to off the screen, then you need to modify only their "final" position by overriding finalAttributesForDisappearingElementAtIndexPath:, but not their "regular position".
If you want them to come back from the side of the screen then you need to modify their position through initialLayoutAttributesForAppearingItemAtIndexPath:
I am creating one application based on FMMoveTableView where I have to drag cell on long press and change its position with in same section and different section.The cell is dragging fine and setting in the same and different section.But the problem is when I start dragging the cell upwards the table also starts scrolling up.So some of its cells are invisible because of bounce where we want to keep the dragged cell.The same thing is happening when I drag the cell to the bottom.
Is it anything related to UITableView property or I have to do it programmatically?
The app FMMoveTableView which I followed for this functionality,it is working fine where it is using UITableView class type.I implemented it in UIViewController class where I made some other views.
UITableView Properties:
self.GroupedTableView=[[UITableView alloc]initWithFrame:CGRectMake(20, 25, 280, 480) style:UITableViewStylePlain];
self.GroupedTableView.showsHorizontalScrollIndicator=YES;
self.GroupedTableView.showsVerticalScrollIndicator=YES;
self.GroupedTableView.bounces=YES;
self.GroupedTableView.alwaysBounceHorizontal=NO;
self.GroupedTableView.alwaysBounceVertical=YES;
self.GroupedTableView.bouncesZoom=YES;
self.GroupedTableView.delaysContentTouches=YES;
self.GroupedTableView.canCancelContentTouches=YES;
self.GroupedTableView.userInteractionEnabled=YES;
self.GroupedTableView.dataSource=self;
self.GroupedTableView.delegate=self;
self.GroupedTableView.rowHeight=30;
self.GroupedTableView.backgroundColor=[UIColor clearColor];
self.GroupedTableView.tag=202;
[self.view addSubview:self.GroupedTableView];
Long Press Gesture:
UILongPressGestureRecognizer *movingGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPress:)];
[movingGestureRecognizer setDelegate:self];
[self.GroupedTableView addGestureRecognizer:movingGestureRecognizer];
Auto Scroll Methods:
- (void)legalizeAutoscrollDistance
{
float minimumLegalDistance = [self.GroupedTableView contentOffset].y * -1;
float maximumLegalDistance = [self.GroupedTableView contentSize].height - ([self.GroupedTableView frame].size.height + [self.GroupedTableView contentOffset].y);
[self setAutoscrollDistance:MAX([self autoscrollDistance], minimumLegalDistance)];
[self setAutoscrollDistance:MIN([self autoscrollDistance], maximumLegalDistance)];
}
- (void)stopAutoscrolling
{
[self setAutoscrollDistance:0];
[[self autoscrollTimer] invalidate];
[self setAutoscrollTimer:nil];
}
- (void)maybeAutoscrollForSnapShotImageView:(FMSnapShotImageView *)snapShot
{
[self setAutoscrollDistance:0];
NSLog(#"Height====%f",[self.GroupedTableView frame].size.height);
NSLog(#"Height====%f",[self.GroupedTableView contentSize].height);
NSLog(#"Frame====%#",NSStringFromCGRect([snapShot frame]));
NSLog(#"Frame====%#",NSStringFromCGRect([self.GroupedTableView bounds]));
// Check for autoscrolling
// 1. The content size is bigger than the frame's
// 2. The snap shot is still inside the table view's bounds
if ([self.GroupedTableView frame].size.height < [self.GroupedTableView contentSize].height && CGRectIntersectsRect([snapShot frame], [self.GroupedTableView bounds]))
{
CGPoint touchLocation = [[self movingGestureRecognizer] locationInView:self.GroupedTableView];
touchLocation.y += [self touchOffset].y;
float distanceToTopEdge = touchLocation.y - CGRectGetMinY([self.GroupedTableView bounds]);
float distanceToBottomEdge = CGRectGetMaxY([self.GroupedTableView bounds]) - touchLocation.y;
if (distanceToTopEdge < [self autoscrollThreshold])
{
[self setAutoscrollDistance:[self autoscrollDistanceForProximityToEdge:distanceToTopEdge] * -1];
}
else if (distanceToBottomEdge < [self autoscrollThreshold])
{
[self setAutoscrollDistance:[self autoscrollDistanceForProximityToEdge:distanceToBottomEdge]];
}
}
if ([self autoscrollDistance] == 0)
{
[[self autoscrollTimer] invalidate];
[self setAutoscrollTimer:nil];
}
else if (![self autoscrollTimer])
{
NSTimer *autoscrollTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 60.0) target:self selector:#selector(autoscrollTimerFired:) userInfo:snapShot repeats:YES];
[self setAutoscrollTimer:autoscrollTimer];
}
}
- (void)autoscrollTimerFired:(NSTimer *)timer
{
[self legalizeAutoscrollDistance];
CGPoint contentOffset = [self.GroupedTableView contentOffset];
contentOffset.y += [self autoscrollDistance];
[self.GroupedTableView setContentOffset:contentOffset];
// Move the snap shot appropriately
FMSnapShotImageView *snapShot = (FMSnapShotImageView *)[timer userInfo];
[snapShot moveByOffset:CGPointMake(0, [self autoscrollDistance])];
// Even if we autoscroll we need to update the moved cell's index path
CGPoint touchLocation = [[self movingGestureRecognizer] locationInView:self.GroupedTableView];
[self moveRowToLocation:touchLocation];
}
- (float)autoscrollDistanceForProximityToEdge:(float)proximity
{
return ceilf(([self autoscrollThreshold] - proximity) / 5.0);
}
I am unable to stop tableview scroll when I drag a cell.What I need that table should not move till the dragged cell has not reached to the top or bottom and then it should scroll to show hidden Cells.
// // ViewController.h // testingApp
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
UILongPressGestureRecognizer *reco; }
#property (nonatomic, weak) IBOutlet UITableView *table;
#end
//
// ViewController.m
// testingApp
#import "ViewController.h"
#implementation ViewController
#synthesize table;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
reco = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(recognize:)];
[self.table addGestureRecognizer:reco];
}
-(void)recognize:(id)sender
{
NSLog(#"recognize");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 5;
}
// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
return cell;
}
#end
I have got the solution.Actually I am using some gestures on my table view cell.So to enable this along with other gestures I was using:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
So this was actually activating the UITableView gestures too when it was not needed.And so When I dragged the cell Image my table also started scrolling along with the image.I misinterpreted it as my implementation for row sliding has got some issue.So code used in question works is fine if someone needs it in future.What I did is added some conditions in the above method and activated it when it was needed.
I have a UIScrollView within a UITableViewCell for being able to scroll through images within a cell. However, as a cell is reused the scroll position/content is reloaded and therefore the cell doesn't remember what scroll position (page) it was on when it comes into view again.
What's the best way to have a scroll view within a UITableView cell and have it maintain position as it comes back into view. The AirBnB app (https://itunes.apple.com/us/app/airbnb/id401626263?mt=8) seems to have accomplished this for example.
You need to keep track of your scroll views' content offset in a property. In the example below, I do this with a mutable dictionary. In cellForRowAtIndexPath:, I give the scroll view a tag and set the controller as the delegate. In the scroll view delegate method, scrollViewDidEndDecelerating:, the scroll view's content offset is set as the object for the key corresponding to the scroll view's tag. In cellForRowAtIndexPath:, I check for whether the indexPath.row (converted to an NSNumber) is one of the keys of the dictionary, and if so, restore the correct offset for that scroll view. The reason I add 1 to the tags is because the table view has its own scroll view which has a tag of 0, so I don't want to use 0 as a tag for one of the cell's scroll views.
So in cellForRowAtIndexPath, you need something like this:
cell.scrollView.tag = indexPath.row + 1;
cell.scrollView.delegate = self;
if ([self.paths.allKeys containsObject:#(indexPath.row + 1)]) {
cell.scrollView.contentOffset = CGPointMake([self.paths[#(indexPath.row + 1)] floatValue],0);
}else{
cell.scrollView.contentOffset = CGPointZero;
}
return cell;
And in the delegate method:
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView.tag != 0)
[self.paths setObject:#(scrollView.contentOffset.x) forKey:#(scrollView.tag)];
}
paths is a property (NSMutableDictionary) that I create in viewDidLoad.
Use my tableView customs cells
homeScrollCell.h
#import <UIKit/UIKit.h>
#import "MyManager.h"
#interface homeScrollCell : UITableViewCell<UIScrollViewDelegate>
{
MyManager *manager;
UIScrollView *__scrollView;
}
-(void)setPage:(int)page;
#property int currentPage;
#end
homeScrollCell.m
#import "homeScrollCell.h"
#implementation homeScrollCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
manager=[MyManager sharedManager];
__scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width,self.bounds.size.height)];
[__scrollView setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
NSInteger viewcount= 3;
for(int i = 0; i< viewcount; i++)
{
CGFloat x = i * self.bounds.size.width;
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(x, 0,self.bounds.size.width,self.bounds.size.height)];
[view setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
UILabel *label=[[UILabel alloc] initWithFrame:CGRectMake(50, 20, 200, 50)];
[label setBackgroundColor:[UIColor redColor]];
label.text=[NSString stringWithFormat:#"Hi, I am label %i",i];
[view addSubview:label];
view.backgroundColor = [UIColor greenColor];
[__scrollView addSubview:view];
}
[__scrollView setBackgroundColor:[UIColor redColor]];
__scrollView.contentSize = CGSizeMake(self.bounds.size.width *viewcount, 100);
__scrollView.pagingEnabled = YES;
__scrollView.bounces = NO;
__scrollView.delegate=self;
[self addSubview:__scrollView];
// Initialization code
}
return self;
}
-(void)setPage:(int)page
{
CGFloat pageWidth = __scrollView.frame.size.width;
float offset_X=pageWidth*page;
[__scrollView setContentOffset:CGPointMake(offset_X, __scrollView.contentOffset.y)];
_currentPage=page;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
static NSInteger previousPage = 0;
CGFloat pageWidth = scrollView.frame.size.width;
float fractionalPage = scrollView.contentOffset.x / pageWidth;
NSInteger page = lround(fractionalPage);
if (previousPage != page) {
_currentPage=page;
[manager setpage:_currentPage ForKey:[NSString stringWithFormat:#"%i",self.tag]];
previousPage = page;
}
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
And use a Singleton file for keeping record or pages inside cell.
MyManager.h
#import <Foundation/Foundation.h>
#interface MyManager : NSObject
+ (id)sharedManager;
-(void)setpage:(int)page ForKey:(NSString*)key;
-(int)getpageForKey:(NSString*)key;
#end
MyManager.m
#import "MyManager.h"
static NSMutableDictionary *dictionary;
#implementation MyManager
#pragma mark Singleton Methods
+ (id)sharedManager {
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
-(void)setpage:(int)page ForKey:(NSString*)key
{
[dictionary setValue:[NSString stringWithFormat:#"%i",page] forKey:key];
}
-(int)getpageForKey:(NSString*)key
{
return [[dictionary valueForKey:key] intValue];
}
- (id)init {
if (self = [super init]) {
dictionary=[[NSMutableDictionary alloc] init];
}
return self;
}
- (void)dealloc {
// Should never be called, but just here for clarity really.
}
#end
And use this custom cell in cellForRow as
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Home Scroll Cell";
homeScrollCell *cell =[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell==nil) {
cell = [[homeScrollCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.tag=indexPath.row;
[cell setPage:[manager getpageForKey:[NSString stringWithFormat:#"%i",indexPath.row]]];
return cell;
}
Set your page inside CustomCell and scroll and after re scrolling get back to previous position will show you page set by you before scrolling.
It will persist your page moved as it is.
Download And Run
rdelmar's answer is missing something. scrollViewDidEndDecelerating will only be called if a users scroll triggers the deceleration behaviour.
A drag only scroll won't call this method so you will get inconsistent behaviour for the user who won't distinguish between the two types of scroll. You need to catch it with scrollViewDidEndDragging:willDecelerate
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (!decelerate) {
self.tableCellContentOffsets[#(scrollView.tag)] = #(scrollView.contentOffset.x);
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
self.tableCellContentOffsets[#(scrollView.tag)] = #(scrollView.contentOffset.x);
}