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
}
Related
In my project, I have a UITableView with different types of cells.
One of these cells contains a horizontally scrolling UICollectionView.
When I run my project, the UICollectionView is correctly populated, but I cannot scroll horizontally, clicking and dragging inside this UICollectionView only allows for me to scroll the tableview vertically.
The desired behaviour would be that a click inside the cell with the UICollectionView would allow the user to scroll horizontally, clicking in any other cell allows for vertical scrolling.
I'm assuming that I need to overwrite/pass on or disable some OnClick event in this cell however I cannot find the correct solution.
Any help pointing me in the good direction would be greatly appreciated :)
The code for creating the concerning TableViewCell looks like this.
#property(nonatomic) HorizontalCollectionViewDelegate *horizontalCollectionViewDelegate;
- (UITableViewCell *) createHorizontalScrollCell:(NSIndexPath *)indexPath tableView:(UITableView *)tableView{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:myCellIdentifier];
MyHorizontalScrollingTableCell *myHorizontalScrollingTableCell = (MyHorizontalScrollingTableCell *)cell;
UICollectionView *horizontalCollectionView = myHorizontalScrollingTableCell.myHorizontalCollectionView;
//Link collectionView to its delegate
self.horizontalCollectionViewDelegate = [[HorizontalCollectionViewDelegate alloc] init];
horizontalCollectionView.delegate = _horizontalCollectionViewDelegate;
horizontalCollectionView.dataSource = _horizontalCollectionViewDelegate;
_horizontalCollectionViewDelegate.data = myData;
//Define the cells in HorizontalCollectionView
UINib *cellNib = [UINib nibWithNibName:#"CollectionViewCell" bundle:nil];
[horizontalCollectionView registerNib:cellNib forCellWithReuseIdentifier:#"horizCell"];
//Define flow of AttachmentsCollectionView
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setItemSize:CGSizeMake(90, 90)];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[horizontalCollectionView setCollectionViewLayout:flowLayout];
return cell;
}
EDIT: I'm using the same HorizontalCollectionViewDelegate elsewhere in my project (just not in a tableview) and over there it is working, so I assume the problem is not there.
EDIT 2: Problem solved, turns out there wasn't a problem (see answer below), will be accepting my answer in two days when I can.
Solved...
Turned out I only needed to add more content to the UICollectionView.
With 2 or 3 elements the Cells did not fill the complete view. Because the swipe animation was not showing I assumed there was an error somewhere.
However just adding more content solved this issue.
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 have a UITableView and am currently working on adding a custom search bar at the top consisting of a UIView with a UITextField inside of it. As is standard for iOS apps, the search bar should only be visible when the table view is scrolled to the top — when scrolling down it should disappear off the screen along with the other cells.
However, I cannot figure out a way to achieve this effect. If I place the search bar at the top of the table view, it will overlay the cell beneath it. If I place it 50 pixels above the table view, it is not possible for the user to select it since it will automatically disappear when the user releases their finger from the screen.
Can someone please enlighten me as to how to achieve this effect?
Usually you just use UISearchBar as a tableViewHeader for your table view. If you want it hidden when the user enters the screen (like it's done in most native apps) you can just set contentOffset for the tableView in viewWillAppear.
And I am pretty sure that's in fact how they do it. And if you think about it it's what tableHeaderView is meant for.
Try something like this:
- (void)viewDidLoad
{
[super viewDidLoad];
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0., 0., 320., 44.)];
self.tableView.tableHeaderView = searchBar;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
CGPoint contentOffset = self.tableView.contentOffset;
contentOffset.y += CGRectGetHeight(self.tableView.tableHeaderView.frame);
self.tableView.contentOffset = contentOffset;
}
Note that in iOS 7 you should not just set the contentOffset of your tableView to CGPointMake(0., CGRectGetHeight(self.tableView.tableHeaderView.frame)) if your viewController has automaticallyAdjustsScrollViewInsets set to YES, since it probably will not be CGPointZero in viewWillAppear:
#dariaa's answer updated for Swift 3:
override func viewDidLoad() {
super.viewDidLoad()
let searchBar = UISearchBar(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 44))
self.tableView.tableHeaderView = searchBar
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
var contentOffset: CGPoint = self.tableView.contentOffset
contentOffset.y += (self.tableView.tableHeaderView?.frame)!.height
self.tableView.contentOffset = contentOffset
}
Though you'll probably need to set the searchBar as a property if you want to use it.
If you only want your search to be visible when the UITableView is scrolled all the way to the top, make a UITableViewCell subclass that houses your UISearchBar. Then, in tableView:cellForRowAtIndexPath, check if the indexPath is (0,0). This is the table view telling you it is creating the cell at the very top, so then just create your search bar cell instead of your default cell.
Code would look something like this:
- (void)viewDidLoad
{
[self.tableView registerClass:[SearchBarCell class] forCellReuseIdentifier:#"SearchBarCell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0 && indexPath.section == 0) {
//this is the cell that displays the UISearchBar
SearchBarCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SearchBarCell"];
return cell;
}
else {
//create your usual table view cell normally
}
}
There may be a cleaner way to determine the row and section of the indexPath, but I'm writing this code off the top of my head and don't recall a better way.
I want to have a UICollectionView where the cells are also UICollectionViews. Is this possible and if so, could someone give me a guide on how to start this? I already have my main UICollectionView, but I’m having trouble implementing another UICollectionView in its UICollectionViewCell.
I did something similar some time ago. Inside your cellForItemAtIndexPath: of your parent CollectionView you have to do something like below. I havent tested this so you might have to add in some code:
ChildCollectionViewController * cv = //Initialize your secondCVController here
if (!cv) {
cv = [self.storyboard instantiateViewControllerWithIdentifier:#"collectionViewID”]; //set your class’s storyboard ID before doing this
cv.view.frame = cell.contentView.bounds;
[self addChildViewController:cv];
[cell.contentView addSubview:cv.view];
[cv didMoveToParentViewController:self];
}
//Some code here for cell contents
return cell;
}
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];