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
Related
I want to make an animation that when you scroll the tabbar, the indicator view follow and when it touch on the text of the tab, that text change color only the part it being touched.
Example like in the picture below.
How can I achieve this ? I have been racking my brain the whole day and no solution come to mind.
https://i.stack.imgur.com/ZrkZy.png
[PageCollectionViewCell.h]//You can change this cell with your favorite style.
#interface PageCollectionViewCell : UICollectionViewCell
/**
title Label
*/
#property (nonatomic, strong) UILabel *pageLabel;
/**
itemWidth
*/
#property (nonatomic, assign) CGFloat itemWidth;
#end
[PageCollectionViewCell.m]
#import "PageCollectionViewCell.h"
#interface PageCollectionViewCell ()
/**
title lab Margin
*/
#property (nonatomic, assign) CGFloat inset;
/**
border color
*/
#property (nonatomic, strong) UIColor *hexColor;
#end
#implementation PageCollectionViewCell
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.inset = 5;
self.hexColor = [UIColor orangeColor];
[self configSubView];
}
return self;
}
- (void)configSubView {
UILabel *pageLabel = [[UILabel alloc] init];
[pageLabel setTextColor:self.hexColor];
pageLabel.layer.borderColor = self.hexColor.CGColor;
pageLabel.layer.borderWidth = 2;
[pageLabel setTextAlignment:NSTextAlignmentCenter];
pageLabel.clipsToBounds = YES;
[self addSubview:pageLabel];
self.pageLabel = pageLabel;
[pageLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self).insets(UIEdgeInsetsMake(self.inset, self.inset, self.inset, self.inset));
}];
self.pageLabel = pageLabel;
}
#pragma mark - set selected status
- (void)setSelected:(BOOL)isSelected {
[self.pageLabel setTextColor:isSelected ? UIColor.whiteColor : self.hexColor];
self.pageLabel.backgroundColor = isSelected ? self.hexColor : UIColor.clearColor;
}
- (void)setItemWidth:(CGFloat)itemWidth {
self.pageLabel.layer.cornerRadius = (itemWidth - 2 * self.inset) / 2;
}
#end
[ViewViewController.m]
#import "ViewViewController.h"
#import "PageCollectionViewCell.h"
static NSString *kCellID = #"cellID";
#interface ViewViewController ()<UICollectionViewDelegate, UICollectionViewDataSource>
#property (nonatomic, strong) NSArray *arr;//page color
#property (nonatomic, strong) UICollectionView *pageCollectionView;
#property (nonatomic, assign) CGFloat itemWidth;//itemWidth
#property (nonatomic, strong) UIScrollView *pageScrollView;
#end
#implementation ViewViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
self.itemWidth = 40;
self.arr = [NSArray arrayWithObjects:[UIColor blueColor],[UIColor redColor],[UIColor yellowColor],[UIColor orangeColor],[UIColor greenColor], nil];
[self configNavigationBar];
[self configPageView];
}
- (void)configNavigationBar {
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
flowLayout.itemSize = CGSizeMake(self.itemWidth, self.itemWidth);
flowLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);
flowLayout.minimumLineSpacing = 0;
UICollectionView *pageCollectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, self.itemWidth * self.arr.count, NavigationBar_Height) collectionViewLayout:flowLayout];
pageCollectionView.dataSource = self;
pageCollectionView.delegate = self;
pageCollectionView.backgroundColor = UIColor.clearColor;
[pageCollectionView registerClass:[PageCollectionViewCell class] forCellWithReuseIdentifier:kCellID];
self.navigationItem.titleView = pageCollectionView;
NSIndexPath *firstIndexpath = [NSIndexPath indexPathForItem:0 inSection:0];
[pageCollectionView selectItemAtIndexPath:firstIndexpath animated:YES scrollPosition:UICollectionViewScrollPositionBottom];
self.pageCollectionView = pageCollectionView;
}
- (void)configPageView {
//define in pch file
CGFloat navigationBar_height = self.navigationController.navigationBar.frame.size.height;
CGFloat statusBar_height = [UIApplication sharedApplication].statusBarFrame.size.height;
CGFloat homeIndicator_Height = (statusBar_height > 20.0 ? 34.0 : 0.0);
CGFloat screen_width = self.view.frame.size.width;
UIScrollView *pageScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, navigationBar_height + statusBar_height, screen_width, self.view.frame.size.height - navigationBar_height - statusBar_height - homeIndicator_Height)];
pageScrollView.pagingEnabled = YES;
pageScrollView.delegate = self;
pageScrollView.showsVerticalScrollIndicator = NO;
pageScrollView.showsHorizontalScrollIndicator = NO;
pageScrollView.contentSize = CGSizeMake(screen_width * self.arr.count, 0);
[self.view addSubview:pageScrollView];
self.pageScrollView = pageScrollView;
for (NSInteger index = 0; index < self.arr.count; index++) {
UIView *pageView = [[UIView alloc] initWithFrame:CGRectMake(index * self.pageScrollView.frame.size.width, 0, self.pageScrollView.frame.size.width, self.pageScrollView.frame.size.height)];
pageView.backgroundColor = self.arr[index];
[self.pageScrollView addSubview:pageView];
}
}
#pragma mark - UICollectionViewDelegate, UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.arr.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
PageCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCellID forIndexPath:indexPath];
cell.pageLabel.text = [NSString stringWithFormat:#"%ld", (long)indexPath.item + 1];
cell.itemWidth = self.itemWidth;
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
CGFloat screen_width = self.view.frame.size.width;
[self.pageScrollView setContentOffset:CGPointMake(indexPath.item * screen_width, 0) animated:YES];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (!scrollView.isDragging && !scrollView.isDecelerating) {
return;
}
CGFloat screen_width = self.view.frame.size.width;
NSIndexPath *currentIndexpath = [NSIndexPath indexPathForItem:((scrollView.contentOffset.x - screen_width / 2) / screen_width) + 1 inSection:0];
[self.pageCollectionView selectItemAtIndexPath:currentIndexpath animated:YES scrollPosition:UICollectionViewScrollPositionBottom];
}
#end
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 am trying to show images on collectionView. I have trouble showing this images as I have created a reusable cell with imageView inside.Those images have to be equally spaced between as I want to show 3 icons on screen at once. I am using 13 icons and it has to be scrollable horizontally through screen.
I am not able to show image in cell and I do not know how to set image cells with spacing between them using just one reusable cell
CustomCollectionViewCell.h
#interface CustomCollectionViewCell : UICollectionViewCell
#property (nonatomic, retain) UIImageView *imageView;
#end
CustomCollectionViewCell.m
#implementation CustomCollectionViewCell
- (UIImageView *) imageView {
if (!_imageView) {
_imageView = [[UIImageView alloc] initWithFrame:self.contentView.bounds];
[self.contentView addSubview:_imageView];
}
return _imageView;
}
- (void)prepareForReuse {
[super prepareForReuse];
[self.imageView removeFromSuperview];
self.imageView = nil;
}
#end
LandingViewController.m
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
CustomCollectionViewCell *cell = [cv dequeueReusableCellWithReuseIdentifier:#"CustomCell" forIndexPath:indexPath];
return cell;
}
Now OP wants horizontal scrollable direction of Collection view.So Again I created small sample project for this.I put 13 images in horizontal scroll direction.It scrolls successfully on horizontal direction of collection view.
Why I post the horizontal scrollable collectionView here is everyone can understand the answer and get the solution easily.I added extra image I mean exactly 13 images(op wants 13 images into collection view). This answer definitely helps you.
HorizontalScrollableCollectionView
Here I set the scrollDirection as Horizontal in CustomImageFlowLayout.m file
CustomImageFlowLayout.h
#import <UIKit/UIKit.h>
#interface CustomImageFlowLayout : UICollectionViewFlowLayout
#end
CustomImageFlowLayout.m
#import "CustomImageFlowLayout.h"
#implementation CustomImageFlowLayout
- (instancetype)init
{
self = [super init];
if (self)
{
self.minimumLineSpacing = 1.0f;
self.minimumInteritemSpacing = 1.0f;
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
}
return self;
}
- (CGSize)itemSize
{
NSInteger numberOfColumns;
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
numberOfColumns = 3;
else{
if([UIApplication sharedApplication].statusBarOrientation==UIInterfaceOrientationPortrait)
numberOfColumns = 4;
else if([UIApplication sharedApplication].statusBarOrientation == UIDeviceOrientationLandscapeRight || [UIApplication sharedApplication].statusBarOrientation == UIDeviceOrientationLandscapeLeft)
numberOfColumns = 4;
}
NSLog(#"The collection view frame is - %#",NSStringFromCGRect(self.collectionView.frame));
CGFloat itemWidth = (CGRectGetWidth(self.collectionView.frame) - (numberOfColumns - 1)) / numberOfColumns;
NSLog(#"The item width is - %f",itemWidth);
return CGSizeMake(itemWidth, itemWidth);
}
#end
Then CustomCell of UICollectionViewCell
CustomCell.xib
CustomCell.h
#import <UIKit/UIKit.h>
#interface CustomCell : UICollectionViewCell
#property (strong, nonatomic) IBOutlet UIImageView *img;
#property (strong, nonatomic) IBOutlet UILabel *lblCollection;
#end
CustomCell.m
#import "CustomCell.h"
#implementation CustomCell
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
#end
Now Storyboard design starts
My collection view name is here collectionviewVerticalHorizontalFlowLayout
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UICollectionViewDelegate,UICollectionViewDataSource>
#property (strong, nonatomic) IBOutlet UICollectionView *collectionviewVerticalHorizontalFlowLayout;
#end
ViewController.m
#import "ViewController.h"
#import "CustomCell.h"
#import "CustomImageFlowLayout.h"
#interface ViewController (){
NSMutableArray *arrayImages;
NSMutableArray *arrayTitles;
CustomCell *cell;
}
#end
#implementation ViewController
#synthesize collectionviewVerticalHorizontalFlowLayout;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
collectionviewVerticalHorizontalFlowLayout.collectionViewLayout = [[CustomImageFlowLayout alloc] init];
collectionviewVerticalHorizontalFlowLayout.backgroundColor = [UIColor clearColor];
arrayImages = [[NSMutableArray alloc]initWithObjects:#"iPhone.png", #"android.png", #"windows.png", #"blackberry.png", #"lenovovibek5note.png", #"redmi.png", #"moto.png", #"sony.png", #"samsung.png", #"oneplus.png",#"redminote4.png",#"oppo.png",#"vivo.png",nil];
arrayTitles = [[NSMutableArray alloc]initWithObjects:#"iPhone", #"Android", #"Windows", #"Blackberry", #"LenovaVikeK5Note", #"Redmi", #"MotoG", #"Sony", #"Samsung", #"OnePlus", #"RedMiNote4",#"Oppo",#"Vivo",nil];
UINib *cellNib = [UINib nibWithNibName:#"CustomCell" bundle:nil];
[collectionviewVerticalHorizontalFlowLayout registerNib:cellNib forCellWithReuseIdentifier:#"cell"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//UICollectionView Data Source 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 = #"cell";
cell = (CustomCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
if(cell==nil){
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = nib[0];
}
cell.img.image = [UIImage imageNamed:(NSString*)[arrayImages objectAtIndex:indexPath.row]];
NSLog(#"The collection view label text is - %#",[NSString stringWithFormat:#"%#",arrayTitles[indexPath.row]]);
cell.lblCollection.text = arrayTitles[indexPath.row];
cell.lblCollection.textColor = [UIColor blackColor];
return cell;
}
//UICollectionView Delegate Method
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"the clicked indexPath.row is - %ld",(long)indexPath.row);
}
#end
Final out put screen shots
First it shows
Then If I scroll horizontally in the collection view it shows
I will give you what you ask.
I wanted to same thing in one application.I successfully implemented this.Just now I tried the sample project for your question.I got what you ask exactly.I show you the code and everything below.
First CustomImageFlowLayout of NSObject Class
CustomImageFlowLayout.h
#import <UIKit/UIKit.h>
#interface CustomImageFlowLayout : UICollectionViewFlowLayout
#end
CustomImageFlowLayout.m
#import "CustomImageFlowLayout.h"
#implementation CustomImageFlowLayout
- (instancetype)init
{
self = [super init];
if (self)
{
self.minimumLineSpacing = 1.0f;
self.minimumInteritemSpacing = 1.0f;
self.scrollDirection = UICollectionViewScrollDirectionVertical;
}
return self;
}
- (CGSize)itemSize
{
NSInteger numberOfColumns;
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
numberOfColumns = 3;
else{
if([UIApplication sharedApplication].statusBarOrientation==UIInterfaceOrientationPortrait)
numberOfColumns = 4;
else if([UIApplication sharedApplication].statusBarOrientation == UIDeviceOrientationLandscapeRight || [UIApplication sharedApplication].statusBarOrientation == UIDeviceOrientationLandscapeLeft)
numberOfColumns = 4;
}
NSLog(#"The collection view frame is - %#",NSStringFromCGRect(self.collectionView.frame));
CGFloat itemWidth = (CGRectGetWidth(self.collectionView.frame) - (numberOfColumns - 1)) / numberOfColumns;
NSLog(#"The item width is - %f",itemWidth);
return CGSizeMake(itemWidth, itemWidth);
}
#end
After that I created Custom Cell for Images
See my Design first
CustomCell.h
#import <UIKit/UIKit.h>
#interface CustomCell : UICollectionViewCell
#property (strong, nonatomic) IBOutlet UIImageView *img;
#property (strong, nonatomic) IBOutlet UILabel *lblCollection;
#end
CustomCell.m
#import "CustomCell.h"
#implementation CustomCell
#end
Then I use above class in my ViewController
Below is my Storyboard Design
Here my CollectionView name is collectionViewVertical
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout>
#property (strong, nonatomic) IBOutlet UICollectionView *collectionViewVertical;
#end
ViewController.m
#import "ViewController.h"
#import "CustomCell.h"
#import "CustomImageFlowLayout.h"
#interface ViewController ()
{
NSMutableArray *arrayImages;
NSMutableArray *arrayTitles;
CustomCell *cell;
}
#end
#implementation ViewController
#synthesize collectionViewVertical;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
collectionViewVertical.collectionViewLayout = [[CustomImageFlowLayout alloc] init];
collectionViewVertical.backgroundColor = [UIColor clearColor];
arrayImages = [[NSMutableArray alloc]initWithObjects:#"iPhone", #"Android", #"Windows", #"Blackberry", #"Lenova", #"Redmi", #"MotoG", #"Sony", #"Samsung", #"OnePlus", nil];
arrayTitles = [[NSMutableArray alloc]initWithObjects:#"iPhone.png", #"android.png", #"windows.png", #"blackberry.png", #"lenovo.png", #"redmi.png", #"moto.png", #"sony.png", #"samsung.png", #"oneplus.png", nil];
UINib *cellNib = [UINib nibWithNibName:#"CustomCell" bundle:nil];
[collectionViewVertical registerNib:cellNib forCellWithReuseIdentifier:#"cell"];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//UICollectionView Data Source 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 = #"cell";
cell = (CustomCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
if(cell==nil){
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = nib[0];
}
cell.img.image = [UIImage imageNamed:(NSString*)[arrayImages objectAtIndex:indexPath.row]];
cell.lblCollection.text = arrayTitles[indexPath.row];
return cell;
}
//UICollectionView Delegate Method
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"the clicked indexPath.row is - %ld",(long)indexPath.row);
}
Finally the output screen
I am making my own drop down menu because I am not able to find an open source one that does exactly what I need.
I have implemented the dropdown as a UIView and am adding it to the superview of the button that is tapped in order to show it.
Code:
ViewController.m
#import "ViewController.h"
#import "MenuView.h"
#interface ViewController () <MenuViewDelegate>
#property (weak, nonatomic) IBOutlet UIView *fakeHeader;
#property (nonatomic, strong) MenuView *menuView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)btnTapped:(id)sender {
NSArray *array = #[#"Item 1", #"Item 2", #"Item 3", #"Item 4"];
NSArray *imgArray = nil;
if (self.menuView == nil) {
self.menuView = [[MenuView alloc] showDropDownWith:sender txtArr:array imgArr:imgArray direction:#"down" delegate:self];
self.menuView.delegate = self;
} else {
[self.menuView hideDropDown:sender];
self.menuView = nil;
}
}
- (void) menuDelegateMethod:(MenuView *)sender {
self.menuView = nil;
}
#end
MenuView.h
#import <UIKit/UIKit.h>
#class MenuView;
#protocol MenuViewDelegate
- (void)menuDelegateMethod:(MenuView *)sender;
#end
#interface MenuView : UIView
#property (nonatomic, retain) id <MenuViewDelegate> delegate;
- (id)showDropDownWith:(UIButton *)button txtArr:(NSArray *)txtArr imgArr:(NSArray *)imgArr direction:(NSString *)direction delegate:(id)delegate;
- (void)hideDropDown:(UIButton *)button;
#end
MenuView.m
#import "MenuView.h"
#import "QuartzCore/QuartzCore.h"
#interface MenuView () <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) UITableView *table;
#property (nonatomic, strong) UIButton *btnSender;
#property (nonatomic, retain) NSString *animationDirection;
#property (nonatomic, retain) NSArray *list;
#property (nonatomic, retain) NSArray *imageList;
#end
#implementation MenuView
- (id)showDropDownWith:(UIButton *)button txtArr:(NSArray *)txtArr imgArr:(NSArray *)imgArr direction:(NSString *)direction delegate:(id)delegate {
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGFloat origin = [UIScreen mainScreen].bounds.origin.x;
CGFloat realHeight = 40 * txtArr.count;
self.btnSender = button;
self.animationDirection = direction;
self.table = (UITableView *)[super init];
if (self) {
// Initialization code
CGRect btn = button.frame;
self.list = [NSArray arrayWithArray:txtArr];
self.imageList = [NSArray arrayWithArray:imgArr];
if ([direction isEqualToString:#"up"]) {
self.frame = CGRectMake(origin, (btn.origin.y - btn.size.height) , width, 0);
self.layer.shadowOffset = CGSizeMake(0, 1);
} else if ([direction isEqualToString:#"down"]) {
self.frame = CGRectMake(origin, (btn.origin.y + btn.size.height + 10), width, 0);
self.layer.shadowOffset = CGSizeMake(0, 1);
}
self.layer.masksToBounds = YES;
self.layer.shadowOpacity = 0.2;
self.table = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, width, 0)];
self.table.delegate = delegate;
self.table.dataSource = self;
self.table.backgroundColor = [UIColor colorWithRed:0.239 green:0.239 blue:0.239 alpha:1];
self.table.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
self.table.separatorColor = [UIColor lightGrayColor];
self.table.backgroundColor = [UIColor whiteColor];
self.table.userInteractionEnabled = YES;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
if ([direction isEqualToString:#"up"]) {
self.frame = CGRectMake(origin, (btn.origin.y - realHeight), width, realHeight);
} else if([direction isEqualToString:#"down"]) {
self.frame = CGRectMake(origin, (btn.origin.y + btn.size.height + 10), width, realHeight);
}
self.table.frame = CGRectMake(0, 0, width, realHeight);
[UIView commitAnimations];
[button.superview addSubview:self];
self.table.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
[self addSubview:self.table];
}
return self;
}
- (void)hideDropDown:(UIButton *)button {
CGRect btn = button.frame;
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGFloat origin = [UIScreen mainScreen].bounds.origin.x;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
if ([self.animationDirection isEqualToString:#"up"]) {
self.frame = CGRectMake(origin, btn.origin.y, width, 0);
} else if ([self.animationDirection isEqualToString:#"down"]) {
self.frame = CGRectMake(origin, (btn.origin.y + btn.size.height + 10), width, 0);
}
self.table.frame = CGRectMake(0, 0, width, 0);
[UIView commitAnimations];
}
#pragma mark - Table View DataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.list count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.textLabel.font = [UIFont systemFontOfSize:15];
cell.textLabel.textAlignment = NSTextAlignmentLeft;
}
cell.textLabel.text = [self.list objectAtIndex:indexPath.row];
cell.backgroundColor = [UIColor colorWithRed:48.0f/255.0f green:48.0f/255.0f blue:48.0f/255.0f alpha:1.0f];
cell.textLabel.textColor = [UIColor lightTextColor];
return cell;
}
#pragma mark - Table View Delegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 40;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self hideDropDown:self.btnSender];
[self myDelegate];
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
// Remove seperator inset
if ([cell respondsToSelector:#selector(setSeparatorInset:)]) {
[cell setSeparatorInset:UIEdgeInsetsZero];
}
// Prevent the cell from inheriting the Table View's margin settings
if ([cell respondsToSelector:#selector(setPreservesSuperviewLayoutMargins:)]) {
[cell setPreservesSuperviewLayoutMargins:NO];
}
// Explictly set your cell's layout margins
if ([cell respondsToSelector:#selector(setLayoutMargins:)]) {
[cell setLayoutMargins:UIEdgeInsetsZero];
}
}
#pragma mark - View Delegate
- (void)myDelegate {
[self.delegate menuDelegateMethod:self];
}
#end
Shows up perfectly but the didSelect method is never called.
I don't have any views over the top of it that would be stealing the touch events.
It seems that UIViews might not be able to be UITableviewDelegates. If that's true I don't know why, when I make the calling view controller the delegate, it still fails to didSelect.
NOTE: I am aware of the animation faux pas by not using newer methods. This is based on old example code. I will update the animation after I get this issue worked out.
Questions:
Is it true that UIViews can not be UITableView Delegates?
If so, how does one make a calling view controller the delegate for the table view that resides in the UIView? Other than the process of setting it up as a UITableViewDelegate and assigning the calling view controller as the delegate at the time of the creation of the table.
Did I miss something in the way I set this up that steals the cell taps so that didSelect does not get called, either in the view or the viewController?
Thanks for the help.
Agree with #Piotr that the menu must be the table's delegate, so replace self.table.delegate = delegate; with self.table.delegate = self; in MenuView.m.
But additionally, MenuView.m never invokes its delegate, which it should upon selection in the tableview....
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self hideDropDown:self.btnSender];
// remove this, since it does nothing
//[self myDelegate];
// replace it with
[self.myDelegate menuDelegateMethod:self];
}
That last line says, tell the delegate of the menu that something happened.
Another problem is that the menu doesn't really tell the delegate what happened. Certainly the delegate will be interested in which item is selected. Consider changing the protocol to something like:
#protocol MenuViewDelegate
- (void)menuView:(MenuView *)sender didSelectOptionAtIndex:(NSInteger)index;
#end
// calling it
[self.myDelegate menuView:self didSelectOptionAtIndex:indexPath.row];
Another alternative is to hand back the selected string to the delegate. This can be found in the tableview's datasource at the indexPath.row.
Finally, its good practice to not-retain your delegate since the customer of the Menu might retain it, resulting in a retain cycle. Instead, declare the delegate:
// notice "weak"
#property (nonatomic, weak) id <MenuViewDelegate> delegate;
As I can see, you are passing ViewController (it is MenuViewDelegate) to showDropDownWith method, and then use it as table delegate. This is not correct.
You should pass self there (same as with data source), because you want MenuView to be delegate of table, not ViewController, right?
self.table.delegate = self;
I have a lot of section and items. When I touch button in cell it create cell with 4 items in 1 cell (like expanded table) touch again cell disappear.
I found WaterfallCollectionView and there I can change height of items.
What is the best way to achieve this???
Structure like:
----- -----
| +| | +| and other rows
| | | |
----- -----
When I touch my button (+) it should be like:
----- ----- -----
| -| |__|__| | +|
| | | | | | |
----- ----- -----
A new cell create with 4 UIImageView's inside 1 cell ( if 4 elements in 1 cell, if more create more cell). If 1 element create this cell but image will be in the top left corner.
Where in cells should be my info (like expanded table)
Is it better to create different type of cells?
I tried something out where a UICollectionViewCell is made to expand by changing its own size. The new 'cells' that pop out when you tap on a cell are not really new cells, but subviews of the original cell. Essentially it works something like this:
Use UICollectionView with a UICollectionViewFlowLayout.
The UICollectionView's delegate conforms to UICollectionViewDelegateFlowLayout and implements -collectionView:layout:sizeForItemAtIndexPath: to recognize unique cell sizes.
The UICollectionView's delegate implements -collectionView:didSelectCellAtIndexPath: where it adjusts the cell's size to reveal or hide the subviews that look like new cells.
A custom object representing the cell's contents and a custom UICollectionViewCell subclass are used to make it easier to perform and keep track of expansion.
I created an example project here which has a cells that expand to show a number's divisors. It looks something like this:
The project is pretty rough and uncommented, and the transition happens without animation, but if you find this approach interesting and cannot follow the code I can clean it up a bit.
And here's the code dump...
CollectionViewController.h
#import <UIKit/UIKit.h>
#interface CollectionViewController : UIViewController
#end
CollectionViewController.m
#import "CollectionViewController.h"
#import "CellDataItem.h"
#import "CollectionViewCell.h"
#interface CollectionViewController () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
#property (strong, nonatomic) UICollectionView *collectionView;
#property (strong, nonatomic) NSMutableArray *cellData;
#end
NSString *const kCollectionViewCellIdentifier = #"Cell";
#implementation CollectionViewController
- (NSMutableArray *)cellData
{
if (!_cellData) {
NSInteger countValues = 20;
_cellData = [[NSMutableArray alloc] initWithCapacity:countValues];
for (NSInteger i = 0; i < countValues; i++) {
CellDataItem *item = [[CellDataItem alloc] init];
item.number = #(arc4random() % 100);
[_cellData addObject:item];
}
}
return _cellData;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor grayColor];
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.itemSize = [CollectionViewCell sizeWithDataItem:nil];
layout.minimumInteritemSpacing = [CollectionViewCell margin];
layout.minimumLineSpacing = layout.minimumInteritemSpacing;
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero
collectionViewLayout:layout];
_collectionView.backgroundColor = [UIColor darkGrayColor];
_collectionView.dataSource = self;
_collectionView.delegate = self;
[self.view addSubview:_collectionView];
[_collectionView registerClass:[CollectionViewCell class] forCellWithReuseIdentifier:kCollectionViewCellIdentifier];
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
self.collectionView.frame = CGRectMake(0.0f, 0.0f, self.view.bounds.size.width, layout.itemSize.height);
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.cellData.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCollectionViewCellIdentifier
forIndexPath:indexPath];
cell.dataItem = [self.cellData objectAtIndex:indexPath.row];
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return [CollectionViewCell sizeWithDataItem:[self.cellData objectAtIndex:indexPath.row]];
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
CollectionViewCell *cell = (CollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
[cell toggleExpansion];
[collectionView reloadData];
}
#end
#import <Foundation/Foundation.h>
#interface CellDataItem : NSObject
#property (strong, nonatomic) NSNumber *number;
#property (nonatomic, readonly) NSArray *divisors;
#property (nonatomic, getter = isExpanded) BOOL expanded;
#end
CellDataItem.m
#import "CellDataItem.h"
#interface CellDataItem ()
#property (strong, nonatomic) NSArray *divisors;
#end
#implementation CellDataItem
+ (NSArray *)divisorsForNumber:(NSNumber *)number
{
NSMutableArray *divisors = [NSMutableArray arrayWithObjects:#(1), number, nil];
float root = pow(number.doubleValue, 0.5);
if (root == roundf(root)) {
[divisors addObject:[NSNumber numberWithInteger:(NSInteger)root]];
}
NSInteger maxDivisor = (NSInteger)root;
for (NSInteger divisor = 2; divisor < maxDivisor; divisor++) {
float quotient = number.floatValue / (float)divisor;
if (quotient == roundf(quotient)) {
[divisors addObject:[NSNumber numberWithInteger:divisor]];
[divisors addObject:[NSNumber numberWithInteger:(NSInteger)quotient]];
}
}
return [divisors sortedArrayUsingSelector:#selector(compare:)];
}
- (void)setNumber:(NSNumber *)number
{
if (_number == number) {
return;
}
_number = number;
self.divisors = [self.class divisorsForNumber:_number];
}
#end
CollectionViewCell.h
#import <UIKit/UIKit.h>
#class CellDataItem;
#interface CollectionViewCell : UICollectionViewCell
+ (CGFloat)margin;
+ (CGSize)sizeWithDataItem:(CellDataItem *)dataItem;
#property (strong, nonatomic) CellDataItem *dataItem;
- (void)toggleExpansion;
#end
#interface ChildView : UIView
- (UILabel *)labelAtIndex:(NSInteger)index;
- (void)clearLabels;
#end
CollectionViewCell.m
#import "CollectionViewCell.h"
#import <QuartzCore/QuartzCore.h>
#import "CellDataItem.h"
#interface CollectionViewCell ()
#property (strong, nonatomic) NSMutableArray *childViews;
#property (strong, nonatomic) UILabel *numberLabel;
#end
NSInteger const kCollectionViewCellSplitCount = 4;
CGFloat const kCollectionViewCellMargin = 20.0f;
CGSize const kCollectionViewCellDefaultSize = {200.0f, 200.0f};
#implementation CollectionViewCell
+ (CGFloat)margin
{
return kCollectionViewCellMargin;
}
+ (CGSize)sizeWithDataItem:(CellDataItem *)dataItem
{
if (dataItem && dataItem.isExpanded) {
CGSize size = kCollectionViewCellDefaultSize;
NSInteger childViewsRequired = [self childViewsRequiredForDataItem:dataItem];
size.width += childViewsRequired * ([self margin] + size.width);
return size;
} else {
return kCollectionViewCellDefaultSize;
}
}
+ (NSInteger)childViewsRequiredForDataItem:(CellDataItem *)dataItem
{
return (NSInteger)ceilf((float)dataItem.divisors.count / (float)kCollectionViewCellSplitCount);
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
_numberLabel = [[UILabel alloc] init];
_numberLabel.textAlignment = NSTextAlignmentCenter;
_numberLabel.layer.borderColor = [UIColor blackColor].CGColor;
_numberLabel.layer.borderWidth = 1.0f;
_numberLabel.backgroundColor = [UIColor whiteColor];
[self.contentView addSubview:_numberLabel];
}
return self;
}
- (void)setDataItem:(CellDataItem *)dataItem
{
if (_dataItem == dataItem) {
return;
}
_dataItem = dataItem;
self.numberLabel.text = [NSString stringWithFormat:#"%i", dataItem.number.integerValue];
if (!dataItem.expanded) {
[self collapse];
} else if (dataItem.expanded) {
[self expand];
}
}
- (void)collapse
{
for (ChildView *view in self.childViews) {
view.hidden = YES;
}
}
- (void)expand
{
NSInteger childViewsRequired = [self.class childViewsRequiredForDataItem:self.dataItem];
while (self.childViews.count < childViewsRequired) {
ChildView *childView = [[ChildView alloc] init];
[self.childViews addObject:childView];
[self.contentView addSubview:childView];
}
NSInteger index = 0;
for (ChildView *view in self.childViews) {
view.hidden = !(index < childViewsRequired);
if (!view.hidden) {
[view clearLabels];
}
index++;
}
for (NSInteger i = 0; i < self.dataItem.divisors.count; i++) {
NSInteger labelsPerChild = 4;
NSInteger childIndex = i / labelsPerChild;
NSInteger labelIndex = i % labelsPerChild;
[[[self.childViews objectAtIndex:childIndex] labelAtIndex:labelIndex] setText:[NSString stringWithFormat:#"%i", [[self.dataItem.divisors objectAtIndex:i] integerValue]]];
}
}
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat const unitWidth = kCollectionViewCellDefaultSize.width;
CGFloat const unitHeight = kCollectionViewCellDefaultSize.height;
CGFloat const margin = [self.class margin];
self.numberLabel.frame = CGRectMake(0.0f, 0.0f, unitWidth, unitHeight);
for (NSInteger i = 0; i < self.childViews.count; i++) {
ChildView *view = [self.childViews objectAtIndex:i];
view.frame = CGRectMake((i + 1) * (margin + unitWidth), 0.0f, unitWidth, unitHeight);
}
}
- (NSMutableArray *)childViews
{
if (!_childViews) {
_childViews = [[NSMutableArray alloc] init];
}
return _childViews;
}
- (void)toggleExpansion
{
self.dataItem.expanded = !self.dataItem.isExpanded;
if (self.dataItem.isExpanded) {
[self expand];
} else {
[self collapse];
}
}
#end
#interface ChildView ()
#property (strong, nonatomic) NSMutableArray *labels;
#end
#implementation ChildView
- (id)init
{
if ((self = [super init])) {
self.backgroundColor = [UIColor lightGrayColor];
}
return self;
}
- (UILabel *)labelAtIndex:(NSInteger)index
{
if (!self.labels) {
self.labels = [NSMutableArray array];
}
while (self.labels.count <= index) {
UILabel *label = [[UILabel alloc] init];
label.textAlignment = NSTextAlignmentCenter;
label.layer.borderColor = [UIColor blackColor].CGColor;
label.layer.borderWidth = 1.0f;
[self.labels addObject:label];
[self addSubview:label];
}
return [self.labels objectAtIndex:index];
}
- (void)clearLabels
{
for (UILabel *label in self.labels) {
label.text = nil;
}
}
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat labelWidth = self.bounds.size.width * 0.5f;
CGFloat labelHeight = self.bounds.size.height * 0.5f;
for (NSInteger i = 0; i < self.labels.count; i++) {
UILabel *label = [self.labels objectAtIndex:i];
NSInteger x = i % 2;
NSInteger y = i / 2;
label.frame = CGRectMake(x * labelWidth, y * labelHeight, labelWidth, labelHeight);
}
}
#end
If you want expandable cells you have to insert the new cells inside the collection view by calling insertItemsAtIndexPaths: on your collection view.
It is not trivial as you have to shift all your index paths, update the number of items in the section to reflect the newly added cells, etc. If you are looking for something simple then the Waterfall example is good. If you want a more complex / configurable solution then you have to rock your own custom layout. That's what I did.