I have a UICollectionView with a lot of cells inside (about 5k+). I want to make pinch to zoom in/out. I've tried to invalidateLayout on every pinch. It's really slow. SO I want to use CGAffineTransformMakeScale but I don't know how to scroll after this.
My code is:
- (void)didReceivePinchGesture:(UIPinchGestureRecognizer *)gesture {
static CGFloat scaleStart;
if (gesture.state == UIGestureRecognizerStateBegan) {
scaleStart = self.venueLayoutZoom;
}
else if (gesture.state == UIGestureRecognizerStateChanged) {
CGAffineTransform transform = CGAffineTransformMakeScale(self.venueLayoutZoom, self.venueLayoutZoom);
self.activeCollectionNode.view.transform = transform;
self.activeCollectionNode.view.contentSize = CGSizeMake(318 * self.venueLayoutZoom, 500 * self.venueLayoutZoom);
}
}
But when is zoomed in I can't scroll left and right. Help me out.
use
#interface ViewController () <UICollectionViewDataSource,
UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
#property (nonatomic,assign) CGFloat scale;
#property (nonatomic,weak) IBOutlet UICollectionView *collectionView;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.scale = 1.0;
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"cell"];
UIPinchGestureRecognizer *gesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(didReceivePinchGesture:)];
[self.collectionView addGestureRecognizer:gesture];
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:
(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:
(NSIndexPath *)indexPath
{
return CGSizeMake(50*self.scale, 50*self.scale);
}
- (void)didReceivePinchGesture:(UIPinchGestureRecognizer*)gesture
{
static CGFloat scaleStart;
if (gesture.state == UIGestureRecognizerStateBegan)
{
scaleStart = self.scale;
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
self.scale = scaleStart * gesture.scale;
[self.collectionView.collectionViewLayout invalidateLayout];
}
}
Related
I added a GIF to describe a problem. I have a UICollectionView with a lot of cells and each cell has a CALayer inside. I have a pinch gesture in my UICollectionView. When it zooming in it looks like each cell zooming apart. See spaces between cells on gif. Is it possible to zoom in each cells together? Thanks in advance
Code:
Cell subclass
#property (nonatomic, strong) CALayer *circularLayer;
- (void)layoutSubviews
{
[self updateRoundedCorners];
}
- (void)updateRoundedCorners
{
CGRect bounds = self.bounds;
self.circularLayer.bounds = bounds;
self.circularLayer.position = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
}
#pragma mark - Property Set
- (void)setCellObject:(VenueLayoutCellObject *)cellObject
{
self->_cellObject = cellObject;
self.objectBackgroundColor = cellObject.objectColor;
self.type = cellObject.type;
}
Controller
- (void)didReceivePinchGesture:(UIPinchGestureRecognizer *)gesture
{
static CGFloat scaleStart;
if (gesture.state == UIGestureRecognizerStateBegan) {
scaleStart = self.venueLayoutZoom;
}
else if (gesture.state == UIGestureRecognizerStateChanged) {
self.venueLayoutZoom = scaleStart * gesture.scale;
[self.activeCollectionView.collectionViewLayout invalidateLayout];
}
}
Updated:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
VenueLayoutCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kVenueLayoutCellReuseIdentifier forIndexPath:indexPath];
self.activeCollectionViewCellsDictionary[indexPath] = cell;
if (self.activeCollectionViewObjects.count > indexPath.section) {
NSArray *rows = self.activeCollectionViewObjects[indexPath.section];
if (rows.count > indexPath.row) {
if ([rows[indexPath.row] isKindOfClass:[VenueLayoutCellObject class]]) {
VenueLayoutCellObject *object = rows[indexPath.row];
cell.cellObject = object;
}
}
}
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(nonnull NSIndexPath *)indexPath
{
CGFloat widthAndHeight = [self widthAndHeightForActiveCollectionViewItem];
return CGSizeMake(widthAndHeight *self.venueLayoutZoom, widthAndHeight * self.venueLayoutZoom);
}
- (CGFloat)widthAndHeightForActiveCollectionViewItem
{
NSArray *array = self.activeCollectionViewObjects;
__block NSInteger maxCount = 0;
[array enumerateObjectsUsingBlock:^(NSArray *subArray, NSUInteger idx, BOOL * _Nonnull stop) {
if (subArray.count > maxCount) {
maxCount = subArray.count;
}
}];
CGFloat widthAndHeight = CGRectGetWidth(self.activeCollectionView.bounds) / maxCount;
return widthAndHeight;
}
The changes you are making to the layer are implicitly animated, meaning that they are performed with an animation, even though you haven't specifically told them to.
Since you're responding to user gestures, you don't want the changes to be animated as this is making them lag behind the gesture.
You can turn off implicit animations during layoutSubviews like this:
- (void)layoutSubviews
{
[super layoutSubviews];
[CATransaction begin];
[CATransaction setDisableActions: YES];
[self updateRoundedCorners];
[CATransaction commit];
}
Cell subclass
#property (nonatomic, strong) CALayer *circularLayer;
- (void)layoutSubviews
{
[super layoutSubviews];
[self updateRoundedCorners];
}
The only problem I see with this code importantly is missing [super layoutSubviews];
If it doesn't work do provide more code.
I have a simple collection view with 5 elements.
The default order of the cells looks like this:
I wanna know if it's possible to change the padding\order of the cells to get result like this:
Option 1: (Clean option)
You should have 2 sections.
Section 1 with 3 cells
Section 2 with 2 cells
You can then adjust the inset with collectionView(_:layout:insetForSectionAt:) for the section you want to adjust. (in this case, section 2)
If you do not implement this method, the flow layout uses the value in
its sectionInset property to set the margins instead. Your
implementation of this method can return a fixed set of margin sizes
or return different margin sizes for each section.
Section insets are
margins applied only to the items in the section. They represent the
distance between the header view and the first line of items and
between the last line of items and the footer view. They also indicate
they spacing on either side of a single line of items. They do not
affect the size of the headers or footers themselves.
Option 2: (Spaghetti option, but good to know)
Create a custom subclass for section 2 items, where you can customize the inset of the actual content for the UICollectionViewCell contentView subviews.
Then in section 2, return your customized cell.
To do this, you may need to write a custom layout. Check out the Collection View Programming Guide for more information.
The default (flow) layout will always lay the cells out in this pattern:
You can achieve this type of format in UICollectionView by changing the number of sections from 1 to 2.
And then you can define your custom UICollectionViewFlowLayout accordingly for different sections.
SOLUTION: I've created a subclass of UICollectionView named "CenteringCollectionView" and with a few calculations of the sections I made it!
The .h file:
#import <UIKit/UIKit.h>
#class CenteringCollectionView;
#protocol CenteringCollectionViewDelegate <NSObject>
-(void)collectionView:(CenteringCollectionView *)collectionView didDequeueReusableCell:(UICollectionViewCell *)cell indexPath:(NSIndexPath *)indexPath;
#end
#interface CenteringCollectionView : UICollectionView
#property (nonatomic, strong) NSMutableArray *dataSourceArr;
#property (nonatomic, weak) id<CenteringCollectionViewDelegate> delegateCenteringCollection;
#end
The .m file:
#import "CenteringCollectionView.h"
#interface CenteringCollectionView () <UICollectionViewDelegate, UICollectionViewDataSource>
#property (nonatomic, assign) IBInspectable long elementsInRow;
#property (nonatomic, assign) long elementsInRowInitialValue;
#property (nonatomic) IBInspectable NSString *cellIdentifier;
#property (nonatomic) IBInspectable CGFloat cellRelativeSize; // 0..1
#property (nonatomic, assign) long numOfSections;
#property (nonatomic, assign) IBInspectable BOOL autoResize; // *** If we want auto resize - we need to set the height constraint of the collection view in size of 1 line only even if we have more than 1 line (section).
#property (nonatomic, assign)IBInspectable CGFloat heightMiddleSpacing;
#property (nonatomic, assign) long cellSize;
//#property (nonatomic, assign) CGFloat verticalTopInset;
#property (nonatomic, assign) CGFloat initialHeightConstraint;
#property (nonatomic, weak) NSLayoutConstraint *selfHeightConstraint;
#property (nonatomic, assign) CGFloat cellSpacing;
#property (nonatomic, assign) BOOL shouldReloadUIElements;
// UI IBInspectable
#property (nonatomic, weak) IBInspectable UIColor *runtimeColor;
#end
static long const maxElementsInRowDefault = 3;
#implementation CenteringCollectionView
-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
self.elementsInRow = maxElementsInRowDefault; // will get the default value if not stored value in storyboard
self.elementsInRowInitialValue = self.elementsInRow;
self.cellRelativeSize = 0.5;
self.initialHeightConstraint = -1;
}
return self;
}
-(void)setDataSourceCount:(long)dataSourceCount
{
if (dataSourceCount == _dataSourceCount)
{
return;
}
_dataSourceCount = dataSourceCount;
self.shouldReloadUIElements = YES;
self.elementsInRow = MIN(self.elementsInRowInitialValue, self.dataSourceCount);
self.numOfSections = ceil((CGFloat)self.dataSourceCount / (CGFloat)self.elementsInRow);
CGFloat selfHeight = [self handleAutoResizeAndReturnTheNewHeightIfNeeded];
CGFloat selfWidth = CGRectGetWidth(self.frame);
CGFloat cellWidth = (selfWidth / self.elementsInRow) * self.cellRelativeSize;
CGFloat cellHeight = (selfHeight / self.numOfSections) * self.cellRelativeSize;
self.cellSize = MIN(cellWidth, cellHeight);
dispatch_async(dispatch_get_main_queue(), ^{
[self setCollectionView];
[self reloadData];
});
}
-(void)awakeFromNib
{
[super awakeFromNib];
self.elementsInRowInitialValue = self.elementsInRow;
[self handleUIelementsIBInspectable];
}
-(void)handleUIelementsIBInspectable
{
if (self.runtimeColor)
{
[self setBackgroundColor:self.runtimeColor];
}
}
-(CGFloat)handleAutoResizeAndReturnTheNewHeightIfNeeded
{
if (self.autoResize)
{
for (NSLayoutConstraint *constraint in [self constraints])
{
if (constraint.firstAttribute == NSLayoutAttributeHeight)
{
if (self.initialHeightConstraint == -1) // not set yet
{
self.initialHeightConstraint = constraint.constant;
}
if (!self.selfHeightConstraint)
{
self.selfHeightConstraint = constraint;
}
CGFloat newHeight = self.initialHeightConstraint * self.numOfSections;
constraint.constant = newHeight;
if (self.bounds.size.height != newHeight)
{
CGRect frame = self.bounds;
frame.size.height = newHeight;
[self setBounds:frame];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.superview layoutIfNeeded];
[self layoutIfNeeded];
});
return newHeight;
}
}
}
return CGRectGetHeight(self.frame);
}
-(long)numOfSpacesInRow
{
return self.elementsInRow + 1;
}
-(long)numOfSpacesBetweenLines
{
return self.numOfSections + 1;
}
-(void)setCellRelativeSize:(CGFloat)cellRelativeSize
{
_cellRelativeSize = MAX(0, MIN(cellRelativeSize, 1));
}
-(void)setCollectionView
{
[self reloadData];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
CGFloat horizontalCellSpacing = ((CGRectGetWidth(self.frame) - (self.cellSize * self.elementsInRow)) / self.numOfSpacesInRow);
CGFloat verticalCellSpacing = (CGRectGetHeight(self.frame) - (self.numOfSections * self.cellSize)) / self.numOfSpacesBetweenLines;
self.cellSpacing = MAX(MIN(horizontalCellSpacing, verticalCellSpacing), 0);
[layout setMinimumInteritemSpacing:self.cellSpacing];
[layout setMinimumLineSpacing:self.cellSpacing];
[layout setScrollDirection:UICollectionViewScrollDirectionVertical];
[self setCollectionViewLayout:layout];
self.showsVerticalScrollIndicator = NO;
self.showsHorizontalScrollIndicator = NO;
self.scrollEnabled = NO;
if (!self.delegate)
{
self.delegate = self;
self.dataSource = self;
}
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(self.cellSize, self.cellSize);
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
BOOL isLastSection = (section == self.numOfSections - 1);
if (isLastSection == NO)
{
return self.elementsInRow;
}
else
{
long numOfLeftItemsInLastRow = self.dataSourceCount % self.elementsInRow;
if (numOfLeftItemsInLastRow == 0)
{
return self.elementsInRow;
}
else
{
return numOfLeftItemsInLastRow;
}
}
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:self.cellIdentifier forIndexPath:indexPath];
if ([self.delegateCenteringCollection respondsToSelector:#selector(collectionView:didDequeueReusableCell:indexPath:)])
{
[self.delegateCenteringCollection collectionView:self didDequeueReusableCell:cell indexPath:[self indexPathWithoutSectionsFrom:indexPath]];
}
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
if ([self.delegateCenteringCollection respondsToSelector:#selector(collectionView:didSelectItemAtIndexPath:cell:)])
{
UICollectionViewCell *selectedCell = [collectionView cellForItemAtIndexPath:indexPath];
[self.delegateCenteringCollection collectionView:self didSelectItemAtIndexPath:[self indexPathWithoutSectionsFrom:indexPath] cell:selectedCell];
}
}
-(NSIndexPath *)indexPathWithoutSectionsFrom:(NSIndexPath *)indexPath
{
long sectionNum = indexPath.section;
long rowNum = sectionNum * self.elementsInRow + indexPath.row;
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:rowNum inSection:0];
return newIndexPath;
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return self.numOfSections;
}
-(void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
dispatch_async(dispatch_get_main_queue(), ^{
if (self.shouldReloadUIElements == NO)
{
return;
}
if (self.autoResize && self.selfHeightConstraint)
{
BOOL isTheFirstCellInTheLastSection = (indexPath.section == self.numOfSections - 1) && indexPath.row == 0;
if (isTheFirstCellInTheLastSection)
{
CGFloat newHeight = CGRectGetMaxY(cell.frame) + self.cellSpacing;
self.selfHeightConstraint.constant = newHeight;
if (self.bounds.size.height != newHeight)
{
CGRect frame = self.bounds;
frame.size.height = newHeight;
[self setBounds:frame];
}
[self.superview layoutIfNeeded];
[self layoutIfNeeded];
}
}
});
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
if (self.shouldReloadUIElements == NO)
{
return collectionView.contentInset;
}
NSInteger cellsCount = [collectionView numberOfItemsInSection:section];
CGFloat horizontalInset = (collectionView.bounds.size.width - (cellsCount * self.cellSize) - ((cellsCount - 1) * self.cellSpacing)) * 0.5;
horizontalInset = MAX(horizontalInset, 0.0);
BOOL isLastSection = (section == self.numOfSections - 1);
CGFloat verticalTopInset = self.cellSpacing;
CGFloat verticalBottomInset = verticalTopInset;
if (section == 0 && isLastSection == NO)
{
if (self.heightMiddleSpacing)
{
verticalBottomInset += self.heightMiddleSpacing;
}
verticalBottomInset /= 2;
}
if (section > 0)
{
if (self.heightMiddleSpacing)
{
verticalTopInset += self.heightMiddleSpacing;
}
verticalTopInset /= 2;
if (isLastSection == NO)
{
if (self.heightMiddleSpacing)
{
verticalBottomInset += self.heightMiddleSpacing;
}
verticalBottomInset /= 2;
}
}
return UIEdgeInsetsMake(verticalTopInset, horizontalInset, verticalBottomInset, horizontalInset);
}
#end
And to make it work, all we need to do in the parent view is:
self.collectionView.delegateCenteringCollection = self;
self.collectionView.dataSourceCount = 5; // Or whatever number we want!
In the storyboard: we need to create collectionView of this class and set the "elements in row" value, also set the "Cell Identifier" and the "Cell relative size" between 0 to 1 (the "Cell relative size" value: will calculate the cell size & paddings according to the collectionView width & height).
And at last - set "autoResize" to "true" if you want that the collection view will resize its own height constraint(if exist) automatically according to the number of rows. If we set "autoResize" to true, the height constraint that we set to the collectionView will determine the height of a single row. If our collectionView should grow for example to 3 rows, it will multiple our collectionview height constraint by 3.
And it works like a charm!
I'm currently building an app that has to present a twitter feed in the form of an horizontal collection view.
3 tweets (cells) should always be visible on screen and centered horizontaly aswell as half a tweet (last element of the previous 3 and first element of the next 3) on both side.
Here is a picture so it's easier to understand
When the user scrolls it should scroll 3 by 3 (or less if there are actualy less cell left to scroll). Sort of a custom paging.
I tried using a Custom layout and it works and make the cells be centered, showing half tweets on both side just like I want. But I can't make it work with the scrolling I want (3by3). And it won't let me show the first or last cell entirely... Anyway, when I had the following code, it just cancel the custom layout behaviour, but scrolls 3 by 3...
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
float pageWidth = ((self.view.frame.size.width / 4)*3);
float currentOffset = scrollView.contentOffset.x;
float targetOffset = targetContentOffset->x;
float newTargetOffset = 0;
if (targetOffset > currentOffset)
newTargetOffset = ceilf(currentOffset / pageWidth) * pageWidth;
else
newTargetOffset = floorf(currentOffset / pageWidth) * pageWidth;
if (newTargetOffset < 0)
newTargetOffset = 0;
else if (newTargetOffset > scrollView.contentSize.width)
newTargetOffset = scrollView.contentSize.width;
targetContentOffset->x = currentOffset;
[scrollView setContentOffset:CGPointMake(newTargetOffset, 0) animated:YES];
}
So guys, has anyone played with this before and could help me get things done ? Thanks a bunch.
ViewController.h
#import <UIKit/UIKit.h>
#import "CustomCell.h"
#import "DetailimageViewController.h"
#interface RootViewController : UIViewController<UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout>
{
IBOutlet UICollectionView *collectionViewHorizontalVertical;
}
ViewController.m
#import "RootViewController.h"
#interface RootViewController ()
{
NSMutableArray *arrayImages;
DetailimageViewController *detailImageVC ;
}
#end
#implementation RootViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
arrayImages = [[NSMutableArray alloc]initWithObjects:#"iMove.jpg",#"iPad.jpg",#"iPhone.jpg",#"iPod.jpg",#"iRing.jpg",#"iTV.jpg",#"iwatch.jpeg",nil];
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
flowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
UINib *cellNib = [UINib nibWithNibName:#"CustomCell" bundle:nil];
[collectionViewHorizontalVertical registerNib:cellNib forCellWithReuseIdentifier:#"cvCell"];
}
//UICollectionView DataSource and Delegate Methods
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return arrayImages.count;
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"cvCell";
CustomCell *cell = (CustomCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.imgViewCollection.image = [UIImage imageNamed:[arrayImages objectAtIndex:indexPath.row]];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"The indexPath is - %ld",(long)indexPath.item);
int ind = (int)indexPath.item;
detailImageVC = [[DetailimageViewController alloc]initWithNibName:#"DetailimageViewController" bundle:nil];
detailImageVC.arrayImagesCount = arrayImages;
detailImageVC.intSelectedIndex = ind;
[[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithFormat:#"%d",detailImageVC.intSelectedIndex] forKey:#"SelectedCollectionViewID"];
[[NSUserDefaults standardUserDefaults] synchronize];
[self.navigationController pushViewController:detailImageVC animated:YES];
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(200, 200);
}
#end
In above coding I have 7 images.So you need to have your images above 7 or less.It is your wish.
Then DetailimageViewController.h
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#interface DetailimageViewController : UIViewController<UIScrollViewDelegate>
{
}
#property (strong, nonatomic) NSMutableArray *arrayImagesCount;
#property (strong, nonatomic) IBOutlet UIScrollView *scroll;
#property (strong, nonatomic) IBOutlet UIPageControl *pageControl;
#property (strong, nonatomic) IBOutlet UIImageView *imageviewScrollView
#property (assign, nonatomic) NSInteger seletedIndex;
#property (strong, nonatomic) UIImage *imageSelection;
#property (nonatomic, assign) int intSelectedIndex;
#property (nonatomic, strong) NSArray *pageImages;
#property (nonatomic, assign) CGFloat lastContentOffset;
- (IBAction)actionBack:(id)sender;
#end
DetailimageViewController.m
#import "DetailimageViewController.h"
#interface DetailimageViewController ()
{
UIImageView *subimg;
}
#end
#implementation DetailimageViewController
typedef enum ScrollDirection
{
ScrollDirectionNone,
ScrollDirectionRight,
ScrollDirectionLeft,
ScrollDirectionUp,
ScrollDirectionDown,
ScrollDirectionCrazy,
} ScrollDirection;
#synthesize arrayImagesCount;
#synthesize scroll;
#synthesize seletedIndex;
#synthesize imageSelection;
#synthesize intSelectedIndex;
#synthesize pageImages = _pageImages;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
scroll.layer.cornerRadius=8.0f;
scroll.layer.masksToBounds=YES;
scroll.layer.borderColor=[[UIColor redColor]CGColor];
scroll.layer.borderWidth= 1.0f;
NSInteger pageCount = arrayImagesCount.count;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int intCollectionViewIndex = [[defaults valueForKey:#"SelectedCollectionViewID"]intValue];
if(intSelectedIndex == intCollectionViewIndex)
{
for (int i=intSelectedIndex; i<[arrayImagesCount count]; i++)
{
CGRect frame;
if(i == intCollectionViewIndex)
{
frame.origin.x = self.scroll.frame.size.width *intCollectionViewIndex;
frame.origin.y=0;
frame.size=self.scroll.frame.size;
subimg=[[UIImageView alloc]initWithFrame:CGRectMake(self.scroll.frame.size.width *intCollectionViewIndex,0,self.scroll.frame.size.width,self.scroll.frame.size.height)];
subimg.image=[UIImage imageNamed:[NSString stringWithFormat:#"%#",[arrayImagesCount objectAtIndex:i]]];
[self.scroll addSubview:subimg];
[self.scroll setContentOffset:CGPointMake(self.scroll.frame.size.width*i,-20) animated:YES];
self.scroll.contentSize = CGSizeMake(self.scroll.frame.size.width*arrayImagesCount.count, self.scroll.frame.size.height);
}
else{
}
}
}
}
ScrollView Delegate Method
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSLog(#" Offset = %# ",NSStringFromCGPoint(scrollView.contentOffset));
ScrollDirection scrollDirection;
if (self.lastContentOffset > scrollView.contentOffset.x)
{
scrollDirection = ScrollDirectionRight;
NSLog(#"The scrollview is scrolling right side");
for(int j=intSelectedIndex;j<[arrayImagesCount count];j--)
{
CGRect frameRight;
frameRight.origin.x = self.scroll.frame.size.width *j;
frameRight.origin.y=0;
frameRight.size=self.scroll.frame.size;
subimg=[[UIImageView alloc]initWithFrame:CGRectMake(self.scroll.frame.size.width *j,0,self.scroll.frame.size.width,self.scroll.frame.size.height)];
subimg.image=[UIImage imageNamed:[NSString stringWithFormat:#"%#",[arrayImagesCount objectAtIndex:j]]];
[self.scroll addSubview:subimg];
self.scroll.contentSize = CGSizeMake(self.scroll.frame.size.width*arrayImagesCount.count, self.scroll.frame.size.height);
}
}
else if (self.lastContentOffset < scrollView.contentOffset.x)
{
scrollDirection = ScrollDirectionLeft;
NSLog(#"The scrollview is scrolling left side");
for(int k=intSelectedIndex;k<[arrayImagesCount count];k++)
{
CGRect frameLeft;
frameLeft.origin.x = self.scroll.frame.size.width *k;
frameLeft.origin.y=0;
frameLeft.size=self.scroll.frame.size;
subimg=[[UIImageView alloc]initWithFrame:CGRectMake(self.scroll.frame.size.width *k,0,self.scroll.frame.size.width,self.scroll.frame.size.height)];
subimg.image=[UIImage imageNamed:[NSString stringWithFormat:#"%#",[arrayImagesCount objectAtIndex:k]]];
[self.scroll addSubview:subimg];
self.scroll.contentSize = CGSizeMake(self.scroll.frame.size.width*arrayImagesCount.count, self.scroll.frame.size.height);
}
}
self.lastContentOffset = scrollView.contentOffset.x;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
CGPoint translation = [scrollView.panGestureRecognizer translationInView:scrollView.superview];
NSLog(#"The translation.x is - %f",translation.x);
NSLog(#"The scrollview content off set is - %f",scrollView.contentOffset.x);
if ([scrollView.panGestureRecognizer translationInView:scrollView.superview].x > 0) {
// handle dragging to the right
NSLog(#"It is dragging to right side");
} else {
// handle dragging to the left
NSLog(#"It is dragging to left side");
}
}
- (IBAction)actionBack:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
#end
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.
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.