I have a collection view populated with data like:
[self.graniteImages addObject:[[NSMutableDictionary alloc]
initWithObjectsAndKeys:#"Angel Cream", #"name",
#"angel-cream.jpg", #"image", nil]];
[self.graniteImages addObject:[[NSMutableDictionary alloc]
initWithObjectsAndKeys:#"Angola Black", #"name" ,
#"angola_black1.jpg", #"image" , nil]];
and then have a segue to a detail view which is static.
I now want to add paging to the detail view so that I can swipe through the images that I have contained in my collection view in the same way that the iPhone photo gallery works.
I'm guessing either a scrollview or a second collectionview would be needed however I'm not sure where to start with this so any pointers, sample code or any other help you guys could give would be massively appreciated.
Thanks
If you already have a collection view that does what you want just turn on paging. self.collectionView.pagingEnabled = YES You can also enable paging in IB by checking the paging box in the attributes inspector. Using a UICollectionView gives you the added benefit of reusable cells.
Edit
You could still use UICollectionView. Create an UICollectionViewCell subclass that contains your detail view. Then when you want to launch directly to a particular image you can use - (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated If you're loading a ton of images in this view the scrollview method will load all of your images at once rather than on demand like they would in a collectionview scenario. You can always work around that in a scrollview but all that work's been done for you in UICollectionView.
Edit2
I started this in a comment but it was getting big...
In your view controller subclass you will need to register either your class or a nib that you created to lay out that cell. I prefer the nib so I can lay everything out in Interface Builder. If you go the nib route you will create an empty xib and drag a UICollectionViewCell out of the object library in the bottom right. Then select that cell and make sure the Class is set to the custom subclass you've created.
Now in your view controller subclass you'll call – registerNib:forCellWithReuseIdentifier: in the viewDidLoad method like this:
[self.collectionView registerNib:[UINib nibWithNibName:#"WhateverYouNamedYourNib" bundle:nil] forCellWithReuseIdentifier:#"cell"];
Your view controller will conform to the UICollectionViewDataSource protocol and you will implement this method:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CustomCellClass *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath
//Setup the cell with image and whatever else
return cell;
}
You should simply use a UIScrollView with paging enabled. The following code should do the trick:
NSInteger numberOfImages = [self.graniteImages count];
CGRect bounds = self.scrollView.bounds;
self.scrollView.contentSize = CGSizeMake(bounds.size.width * numberOfImages, bounds.size.height);
scrollView.pagingEnabled = YES;
CGFloat x = 0;
for (NSMutableDictionary *dict in images) {
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[dict valueForKey:#"image"]]];
imageView.frame = CGRectMake(x, 0, bounds.size.width, bounds.size.height);
[scrollView addSubview:imageView];
x += self.view.bounds.size.width;
}
EDIT: This code would go in your detail view controller's ViewDidLoad method.
Your detail view controller should also have a property index of type NSUInteger and a graniteImages property of type NSMutableArray* that you can set in your collection view controller's prepareForSegue method just like:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if( [segue.identifier isEqualToString:#"yourSegue"]){
((DetailViewController*)segue.destinationViewController).graniteImages = self.graniteImages;
((DetailViewController*)segue.destinationViewController).index = self.tappedImageIndex;
}
}
and in your ViewDidLoad method you add:
[scrollView scrollRectToVisible:CGRectMake(bounds.size.width * self.index, 0, bounds.size.width, bounds.size.height) animated:NO];
Related
I have a collection view, and inside the collection view cell, I have a scrollView in which I want to display some pictures.
I placed a ScrollView inside my collectionviewcell in the storyboard and initialised the ScrollView inside my -collectionView:cellForItemAtIndexPath method as follows:
UIScrollView *scrollView = [cell viewWithTag:20];
scrollView.delegate = self;
[scrollView setContentSize:CGSizeMake((cell.frame.size.width*images.count), scrollView.frame.size.height)];
scrollView.userInteractionEnabled = NO;
[cell addGestureRecognizer:imagesScrollView.panGestureRecognizer];
Only the first image from the images array should be loaded when the view is first loaded and the rest of the images need to be loaded dynamically when the user scrolls the scrollview, in the -scrollViewDidScroll method. However, these images depend on the index of the collectionviewcell that my scrollView is lying in.
How can I access the index of the collectionviewcell from the -scrollViewDidScroll method?
Also, does this seem like a viable method to achieve what I'm trying to do, or will I need to subclass UICollectionViewCell?
Subclassing can be one of the solutions but you can get the indexPath of a UICollectionViewCell from its subviews too. The following code would hopefully serve your needs. Use it in your scrollViewDidScroll: method.
UIView *superview = scrollView;
while (![superview isKindOfClass:[UICollectionViewCell class]]) {
superview = superview.superview;
}
UICollectionViewCell *cell = (UICollectionViewCell *)superview;
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
NSLog(#"IndexPath: %#", indexPath);
Make sure you are not using a scrollView elsewhere in this viewController.
Hello people of stackoverflow,
Currently working on an App where I have a user profile that looks like this
http://i.imgur.com/H1N0ouX.jpg (sorry I dont have enough reputation to post the image)
Header View is a picture of the user.
Tab View is 2 tabs, one with a collectionview, the other with a tableview.
All those views are nested inside a scrollView
My problem is that when I scroll that view (collection view height set programmaticaly depending on number of elements), the collection view's index does not increment. Only the 6 first items are loaded in in the visible cells index of the collection view.
I need to segue from the collectionview's items to the item's detail view.
How can I use the scrollView delegate to know where my collection view is at.
I've looked at those threads :
iOS 7 Collection View inside Scroll View
How to implement non-scrollable UICollectionView inside UIScrollView?
But none gave me the answer I was looking for.
I also tried looking at scrollViewDidEndDecelerating and setting my collection view scroll offset according to the scroll view offset, but I could not make it work.
Any advices?
Thanks
EDIT :
Hello and thanks for your answer,
To answer your question, I can scroll my collectionview, since it is inside a scroll view, I set my collectionview height during viewdidload (based on number of elements)
my problem is, I can click on the first 6 items , and display details (via a segue) but after that, selection is not recognized as the index is not refreshed for my collection view. I can scroll all the way to the bottom of my collection view (I scroll via the scrollView).
My collectionView has user interaction enabled checked, as I said, I can select the first 6 items, and then selection is not recognized.
I understand the UICollectionViewFlowLayout issue, but where do I write this line ? in the viewDidLoad? or in delegate funcs of CollectionView ?
If you need more information, I can copy some code or show you the layout of the view in storyboard
Thanks in advance.
EDIT 2 :
images , storyboard : layout of my view
http://i.gyazo.com/c491786507db2effc702d910020515a2.png
code, here are the datasource funcs of the collectionView
http://gyazo.com/3730caeb2b2a40fef9efec559171744f
delegate func of the collection view
http://i.gyazo.com/81fc9443b4367ecbd304e608ab0cc864.png
EDIT3:
Okay so basically this is what I want to reproduce,
tab view in the middle with multiples tabs with collection view, all this scrollable.
What I cant understand, is if I set my topView as the header of the CollectionView, I can't switch tabs like I want since its inside the header.
ViewController with TalbeView and CollectionView tabs like Google+ in IOS
Hi First of all I need more detail about your question but I am assuming that you want your UICollectionView scrollable and actually its not scrolling.
So could you check following points :-
UserEnableInteraction for collection view should be enabled.
And also check what is the value you gave for UICollectionViewFlowLayout because just consider if You have total 5 elements to show and your UICollectionViewFlowLayout size is like it can show all five items in visible cell index then it doesn't scroll so you need to increase the size of UICollectionViewFlowLayout to make it scroll.
e.g
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setItemSize:CGSizeMake(148, 148)];
And if you want then you can get the help from UIScrollViewDelegate method like
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (!decelerate)
{
// do your job
}
}
set delegates and Datasource in .h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UITableViewDataSource , UITableViewDelegate ,
UIScrollViewDelegate , UICollectionViewDataSource , UICollectionViewDelegateFlowLayout>
You can do that .m
in ViewDidLoad:-
UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
UICollectionView *collectionView =[[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout ];
collectionView.frame = CGRectMake(5, 61, 365, 678);
collectionView.delegate = self;
collectionView.dataSource = self;
[collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:#"cellIdentifier"];
[self.view addSubview:collectionView];
And then Call her delegates method :-
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [arrImages count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
[self createCell:cell forTableview:collectionView];
[self fillCell:cell cellForRowAtIndex:indexPath];
return cell;
}
//- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
//{
// return CGSizeMake(50, 50);
//}
I was trying to implement a view like twitter's iOS app. The view controller contains a scroll view can scroll horizontally, in each page of the scrollview, there is a tableview. For simplicity I just created two UITableView, and add them inside two UIView containers, because besides tableView, I also have some view to show, so the tableView and all other views are added as subViews of the viewContainer for each page.
The view hierarchy is like this:
UIViewController
--view
--scrollView
--view1
--tableView1
--someOtherViews
--view2
--tableView2
--someOtherViews
In the viewDidLoad methods, I implemented something like this:
-(void)viewDidLoad
{
//Some other setup...
scrollView = [[UIScrollView alloc]initWithFrame(0,y0,self.view.frame.size.width,self.view.frame.size.height-offset)];
scrollView.pageEnabled = YES;
scrollView.contentSize = CGSizeMake(self.view.frame.size.width * 2, self.view.frame.size.height - offset);
[self.view addSubView:scrollView];
view1 = [[UIView alloc]initWithFrame:CGRectMake(0,0,self.view.frame.size.width,self.view.frame.size.height-offset)];
tableView1 = [[UITableView alloc]initWithFrame:CGRectMake(0,0,self.view.frame.size.width,self.view.frame.size.height-offset) style:UITableViewStyleGrouped];
tableView1.delegate = self;
tableView1.dataSource = self;
//other setup...
[view1 addSubView:tableView1];
[scrollView addSubView:view1];
view2 = [[UIView alloc]initWithFrame:CGRectMake(self.view.frame.size.width,0,self.view.frame.size.width,self.view.frame.size.height-offset)];
tableView2 = [[UITableView alloc]initWithFrame:CGRectMake(0,0,self.view.frame.size.width,self.view.frame.size.height-offset) style:UITableViewStyleGrouped];
tableView2.delegate = self;
tableView2.dataSource = self;
//other setup...
[view2 addSubView:tableView2];
[scrollView addSubView:view2];
}
For test purpose, I populate the same datasource for both tables. The problem is if I only add one tableView container (for instance view1, which includes tableView1) as the subView of the scrollView, the view shows perfect, one can page to second page, which is blank, and the view controller can be load and show. The same for the second one.
However, if I use the code above, which adds both view containers as the subView of the scrollView, the app would crash when load this view controller. However, if I add the second view container (view2 or view1), but in the view container, I am not adding tableView as its subView, but has other subViews, it works perfectly.
So I figured the problem probably comes from two tableViews. However, I have tried to debug this for a few hours, and still no luck. Anyone can help me?
Thanks!!
I think it is something with you UITableViewDelegate or DataSource methods.
Probably it is the reason you crash.
-Suggestion:
try to make your UITableViews as an iVar and in dataSource methods
specify the tableView you want
like:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section
{
if(tableView == _tableView1)
return _table1DataSource.count;
else
return _table2DataSource.count;
}
try to do it for other methods.
I am trying to make a view controller which deals with the user login. Since I needed the view controller to be scrollable, contain a separate view (for the login), and contain a background, I decided to go with the route of making a tableviewcontroller, subclassing it, and than adding in the necessary views. I subclassed UITableViewController and added this code into the viewdidload()
UIImageView *tempImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"TableViewControllerBlurred.png"]];
[tempImageView setFrame:self.tableView.frame];
self.tableView.backgroundView = tempImageView;
[tempImageView release];
This successfully added my background image to the controller and at this point, the view controller looked like: http://imgur.com/ST4H8uf as it was supposed to.
Moving on, I began working with static cells, dropped in a view into one of the cells and began to design the sign in screen. At this point, my storyboard looked like: http://imgur.com/n6GKeGq&ST4H8uf but the problem comes about when I run the project.
When I run the project, I keep getting the same background screen as seen in the first image without any of the new static cells or views. All and any help is much appreciated as to what may be the cause of this problem. Thank you.
CellForRowAtIndexPath Code:
*/
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:<##"reuseIdentifier"#> forIndexPath:indexPath];
// Configure the cell...
return cell;
}
*/
If what you want is a UITableView with just static cells, then learn to use UIScrollView with a UIViewController.
#interface vc : UIViewController
#property (nonatomic, strong) UIScrollView *scrollView;
#end
#implementation vc
- (id)init // or whatever initializer you are using to make your view controller
{
self = [super init];
if (self) {
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0,320,568)];
[_scrollView setContentSize:CGSizeMake(320,568)]; // equals one screen
[_scrollView setContentSize:CGSizeMake(320,568*2)]; // equals two screens, etc
// contentSize property determines how much you can scroll inside the UIScrollView view if that makes any sense to you.
[self.view addSubview:_scrollView]
// one way of adding a background
UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"imageName"]];
[self.view addSubview:backgroundImageView];
[_scrollView addSubview:[self newStaticCellAtPosition:CGRectMake(0,0,320,45)]];
[_scrollView addSubview:[self newStaticCellAtPosition:CGRectMake(0,45,320,45)]];
// add subviews, you can even use UITableViewCell if you want.
// I'd use simple UIView's and draw separators and whatnot myself if I were you.
}
return self;
}
- (UIView *)newStaticCellAtPosition:(CGRect)position
{
UIView *staticCell = [[UIView alloc] initWithFrame:position];
[staticCell setBackgroundColor:[UIColor redColor]];
return staticCell;
}
#end
For other properties you should check out UIScrollView documentation. Remember UITableView inherits from UIScrollView so if it's easy to pick and choose what you want.
first check datasource and delegate of tableview has to be set.
You might be geeting a problem beacuse of that.
Never use a UITableViewController! In almost every case I have come across it is much much easier to use a UIViewController and add a table view. You simply cannot get at the backgroundView of a UITableViewController and have it scroll properly. I realize that you can only make a "static" table view with a UITableViewController but its simple enough to mimic the exact same behaviour with a regular table view and you don't have to deal with the headache of not being able to add views behind the table (like a background image).
If you use a UISearchDisplayController with a UITableViewController, when the user taps the search bar it animates up to replace the nav bar.
I'd like to get that same effect when using a UISearchBar at the top of a UICollectionViewController. Any ideas?
I had to add the searchBar programmatically as a subview of the UICollectionReusableView, I could never getting it working through IB as I kept getting prototype error when assigning an outlet. Adding the search bar in the implementation file worked for me.
The relevant methods are the following.
-(void)viewDidLoad
{
[super viewDidLoad];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:CellIdentifier];
_objectChanges = [NSMutableArray array];
_sectionChanges = [NSMutableArray array];
[self performFetch];
searchBar = [[UISearchBar alloc]
initWithFrame:CGRectMake(0.0, 50.0, self.view.bounds.size.width,
44.0)];
searchBar.placeholder = #"Search for channels";
searchBar.tintColor = [UIColor blackColor];
searchBar.delegate = self;
}
-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
SupplementaryView *header = nil;
if ([kind isEqual:UICollectionElementKindSectionHeader])
{
header = [self.collectionView dequeueReusableSupplementaryViewOfKind:kind
withReuseIdentifier:#"reuseHeader"
forIndexPath:indexPath];
[header addSubview:searchBar];
}
return header;
}
I just had the same inquiry, and I came up with a half-baked but working solution that does not involve rewriting UISearchDisplayController.
(END RESULT: A UITableView --that answers to shouldReloadTableForSearchString-- overlaid on top of the UICollectionView, once you click search it's dismissed, and you have your results in the collectionView. Read on if this is of interest)
In the IB created a UIViewController in which I inserted(see screenshot):
a view for layout purposes --> in which I first dropped a UISearchBar and display controller.
In the same view (side by side) I dropped a UICollectionView with a custom UICollectionViewCell. Then I dropped in a UITableViewProvider (with a custom UITableCell, but that's not required, you can also ignore the UITabBarItem and the Nav item in the screenshot, that's inconsequential )
I set the height of the UITableView to 0, and wired all the Outlets and delegates, and the net result is the following, when the cursor enters the UISearchBox, the UITableView overlay on top the UICollectionView, as one types the shouldReloadTableForSearchString del gets called and the results appear in the tableView; On searchBarSearchButtonClicked I simply set the dataSource of the UICollectionView and call reloadData on it's outlet, et Voila.
One would think Apple should generecize the search display controller; maybe in a future release?
( This works because that's the way the UISearchDisplay controller is optimized, IIRC there's actually one UITableView per character entry stacked on top of each other )
(Not posting code because there's quite a bit involved; plz inquire if anything is not straightforward)
Here just a swift code - worked for me!
func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
let headerView = self.mainPictureCollectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "headerView", forIndexPath: indexPath)
headerView.addSubview(searchBar)
return headerView
}