I am using setContentOffset on a UITableView because I want to initially hide a search field that is my tableHeaderView.
[self.tableView setContentOffset:CGPointMake(0, 56)]; // No scroll please!
Each time I push a new viewController I want to hide the search bar with contentOffset. But when I pop a viewController that offset is no longer in effect for some reason and shows the search bar. Why is this?
you can try and implement it on the following
- (void)viewWillAppear:(BOOL)animated {
[self.tableView setContentOffset:CGPointMake(0, 56)];
}
that will put the table in the correct position before it is displayed on the screen, I am assuming you mean no animation while setting the position.
I am guessing that you want to stop the user being able to scroll to the very top of the screen. If so you can implement the following UITableView delegate (on iOS5 and above):
scrollViewWillEndDragging:withVelocity:targetContentOffset:
which allows you to modify the final target for a change in the contentOffset. In the implementation you do:
- (void)scrollViewWillEndDragging:(UIScrollView *)theScrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
if(targetContentOffset->y < 56) {
targetContentOffset->y=56;
}
}
If you are trying to preserve the value of something during an action that loses it, the natural solution is to hold onto it yourself ("Hold/Restore"):
"Hold": get content offset to a field or local variable. Apple doc
.. do whatever you want.
"Restore": set content offset to the value you got above.
(Sorry, I don't write Objective C code, so can't provide the exact code. An edit to add the code, would be welcome.)
In a different situation, it might be necessary to hold the row you were at, and then scroll back to that row:
(Adapted from: https://stackoverflow.com/a/34270078/199364)
(Swift)
1. Hold current row.
let holdIndexPath = tableView.indexPathForSelectedRow()
.. do whatever (perhaps ending with "reloadData").
Restore held row:
// The next line is to make sure the row object exists.
tableView.reloadRowsAtIndexPaths([holdIndexPath], withRowAnimation: .None)
tableView.scrollToRowAtIndexPath(holdIndexPath, atScrollPosition: atScrollPosition, animated: true)
Related
First things first:
I do NOT Want to reload whole CollectionView.
I also do NOT want to reload the section either (since it is same as reloadData because my cv only has 1 section).
I put some controls in the Supplementary View since this view acts as a header view. On some case, I want to hide/show the controls as needed. In order to do that I need to reload the Supplementary View as the data for it is already updated.
What I have tried:
UICollectionViewLayoutInvalidationContext *layoutContext =
[[UICollectionViewLayoutInvalidationContext alloc] init];
[layoutContext invalidateSupplementaryElementsOfKind:UICollectionElementKindSectionHeader
atIndexPaths:#[[NSIndexPath indexPathForRow:0 inSection:0]]];
[[_collectionView collectionViewLayout] invalidateLayoutWithContext:layoutContext];
This crash of course. The code doesn't look right either but I am not sure how to construct the UICollectionViewLayoutInvalidationContext properly and telling the collectionview to reload just the Supplementary View.
Thanks.
If you need only to update the header view of a particular section, then you can do something similar to this:
if let header = collectionView.supplementaryView(forElementKind: UICollectionElementKindSectionHeader, at: IndexPath(item: 0, section: indexPath.section)) as? MyCollectionHeaderView {
// Do your stuff here
header.myLabel.isHidden = true // or whatever
}
Code fragment IndexPath(item: 0, section: indexPath.section) may seem a bit weird, but it turns out that the supplementaryView is only returned on the first item in the section.
I am not sure how you have grouped the cells of the collection view.
The simplest solution would be to reload the particular cell using:
- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths
This question has been asked a few times but none of the answers are detailed enough for me to understand why/how things work. For reference the other SO questions are:
How to update size of cells in UICollectionView after cell data is set?
Resize UICollectionView cells after their data has been set
Where to determine the height of a dynamically sized UICollectionViewCell?
I'm using MVC but to keep things simple lets say that I have a ViewController that in ViewWillAppear calls a web service to load some data. When the data has been loaded it calls
[self.collectionView reloadData]
The self.collectionView contains 1 UICollectionViewCell (let's call it DetailsCollectionViewCell).
When self.collectionView is being created it first calls sizeForItemAtIndexPath and then cellForItemAtIndexPath. This causes a problem for me because it's only during cellForItemAtIndexPath that I set the result of the web service to DetailsCollectionViewCell via:
cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"detailsCell" forIndexPath:indexPath];
((DetailsCollectionViewCell*)cell).details = result;
DetailsCollectionViewCell has a setter for the property details that does some work that I need to happen first to know what the correct cell size should be.
Based on the linked questions above it seems like the only way to fire sizeForItemAtIndexPath after cellForItemAtIndexPath is to call
[self.collectionView.collectionViewLayout invalidateLayout];
But this where the other questions don't work for me because although it calls sizeForItemAtIndexPath and allows me to grab enough information from DetailsCollectionViewCell to set the correct height it doesn't update the UI until after the user scrolls the UICollectionView and my guess is that it has something to do with this line from the documentation
The actual layout update occurs during the next view layout update cycle.
However, i'm stumped on how to get around this. It almost feels like I need to create a static method on DetailsCollectionViewCell that I can pass the web service result to during the first sizeForItemAtIndexPath pass and then just cache that result. But i'm hoping there is a simple solution to having the UI automatically update instead.
Thanks,
p.s. - First SO question so hope i followed all the rules correctly.
Actually, from what I found, calling to invalidateLayout will cause calling sizeForItemAtIndexPath for all cells when dequeuing next cell (this is for iOS < 8.0, since 8.0 it will recalculate layout in next view layout update).
So the solution i came up with, is subclassing UICollectionView, and overriding layoutSubviews with something like this:
- (void)layoutSubviews
{
if ( self.shouldInvalidateCollectionViewLayout ) {
[self.collectionViewLayout invalidateLayout];
self.shouldInvalidateCollectionViewLayout = NO;
} else {
[super layoutSubviews];
}
}
and then calling setNeedsLayout in cellForItemAtIndexPath and setting shouldInvalidateCollectionViewLayout to YES. This worked for me in iOS >= 7.0. I also implemented estimated items size this way. Thx.
Here my case and solution.
My collectionView is in a scrollView and I want my collectionView and her cells to resize as I'm scrolling my scrollView.
So in my UIScrollView delegate method : scrollViewDidScroll :
[super scrollViewDidScroll:scrollView];
if(scrollView.contentOffset.y>0){
CGRect lc_frame = picturesCollectionView.frame;
lc_frame.origin.y=scrollView.contentOffset.y/2;
picturesCollectionView.frame = lc_frame;
}
else{
CGRect lc_frame = picturesCollectionView.frame;
lc_frame.origin.y=scrollView.contentOffset.y;
lc_frame.size.height=(3*(contentScrollView.frame.size.width/4))-scrollView.contentOffset.y;
picturesCollectionView.frame = lc_frame;
picturesCollectionViewFlowLayout.itemSize = CGSizeMake(picturesCollectionView.frame.size.width, picturesCollectionView.frame.size.height);
[picturesCollectionViewFlowLayout invalidateLayout];
}
I had to re set the collectionViewFlowLayout cell size then invalidate his layout.
Hope it helps !
I making an app with a table view and a data source (core data). In this table i group several tasks ordered by date, and i have this segmented control.
I want the table to only load the tasks later or equal than today's date, when the user taps the second segment i want to show all tasks, if he taps the first segment the table must only show the later dates tasks again.
The problem is:
1 - I'm using fetchedResultsController associate with a indexPath to get the managed object.
2 - I use the insertRowsAtIndexPaths:withRowAnimation: and deleteRowsAtIndexPaths:withRowAnimation: methods to make the cells appear and disappear. And this mess with my indexPaths, if i want to go to the detail view of an specific row it is associate with a different indexPath, after delete the rows.
This problem was fixed by a method i did, but i still have other problems of indexPaths and cells, and it seems to me that is gone be me messy to each problem a fix.
There is a simple way to do that?
I tried just to hide the cells instead of delete, it works just fine, but in the place of the hidden cells was a blank space, if there is a way to hide these cells and make the non-hidden cells occupy the blank space i think that will be the simplest way.
Anyone can help me?
set the height of the cell to 0 when it hides, and set the height back to the original value when it appears.
TableViewController.h
#interface TableViewController{
CGFloat cellHeight;
}
TableViewController.m
- (void)cellHeightChange{
//if you need hide the cell then
cellHeight = 0;
cellNeedHide.hidden = YES;
//if you need hide the cell then
cellHeight = 44; // 44 is an example
cellNeedHide.hidden = NO;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
switch (section) {
// for example section 0 , row 0 is the cell you wanna hide.
case 0:
switch (row) {
case 0:
return cellHeight;
}
}
}
When the user taps on a segment execute a new fetch request on your managed object to give you an appropriate array (either an array of all dates, or the greater/equal dates). Then use reloadData on the tableView using this new array in the datasource.
or
Give the cell's you wish to hide a height of 0?
I've got a UITableView with the ability to delete the rows using edit mode etc. As standard, when you tap the 'edit' button, you go into edit mode and the content of the cells gets moved to the right. If you do a 'swipe to delete', the cell content stays where it is.
What I want to do is increase the indentation when you enter edit mode. I've tried the UITableView delegate method indentationLevelForRowAtIndexPath but that doesn't seem to work when I'm using a UITableViewCell subclass.
In the end I used the layoutSubviews method in my UITableViewCell subclass. Below is my code:
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect b = [self bounds];
if(self.editing && !self.showingDeleteConfirmation){
b.origin.x = 42;
}
[self.contentView setFrame:b];
}
This indents the cell content further when you enter edit mode and thanks to the "!self.showingDeleteConfirmation", when you do a 'swipe to delete', it doesn't indent it.
However, when you tap the 'edit' button, then tap one of the circle delete buttons, the cell content slides back to the original 0 x axis position. This is because the showingDeleteConfirmation is now set to true.
I've tried to fix this by checking what the current origin.x value is, but every time I check, it's set to 0.
Is there a way I can achieve what I want?
I'm writing a dynamic wizard application using cocoa/objective c on osx 10.6. The application sequences through a series of views gathering user input along the way. Each view that is displayed is provided by a loadable bundle. When the app starts up, a set of bundles are loaded and as the controller sequences through them, it asks each bundle for its view to display. I use the following to animate the transition between views
[[myContentView animator] replaceSubview:[oldView retain] with:newView];
This works fine most of the time. Every once in a while, a view is displayed and some of the subviews are not displayed. It may be a static text field, a checkbox, or even the entire set of subviews. If, for example, a checkbox is not displayed, I can still click where it should be and it then gets displayed.
I thought it might have something to do with the animation so I tried it like this
[myContentView replaceSubview:[oldView retain] with:newView];
with the same result. Any ideas on what's going on here? Thanks for any assistance.
I don't think is good to use this [oldView retain]. This retain do not make sense. The function replaceSubview will retain it if it is necessary.
Because it work "most of the time" I think it is a memory problem. You try to use a released thing. Test without it and see what happens.
I got the same problem, replaceSubview didn't work.
Finally i found something wrong with my code. so here are the rules :
Both subviews should be in MyContentview's subviews array.
OldView should be the topmost subview on MyContentView or replacesubview will not take place.
here is a simple function to perform replacesubview
- (void) replaceSubView:(NSView *)parentView:(NSView *)oldView:(NSView *)newView {
//Make sure every input pointer is not nill and oldView != newView
if (parentView && oldView && newView && oldView!=newView) {
//if newview is not present in parentview's subview array
//then add it to parentview's subview
if ([[parentView subviews] indexOfObject:newView] == NSNotFound) {
[parentView addSubview:newView];
}
//Note : Sometimes you should make sure that the oldview is the top
//subview in parentview. If not then the view won't change.
//you should change oldview with the top most view on parentview
//replace sub view here
[parentView replaceSubview:oldView with:newView];
}
}