I have a weird bug. I have a grouped UITableView with static cells and I want to add a white shade under each section. The way I thought about doing it is to add a shadow to each lowest cell in a section.
So in viewDidAppear (because in viewDidLoad it wouldn't work) I wrote:
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
// change the cells shadow
The problem is that in viewDidLoad I call becomeFirstResponder on a text field. When cellForRowAtIndexPath is called, the textfield looses focus.
I tried using becomeFirstResponder in viewDidAppear after the call to cellForRowAtIndexPath but that doesn't help, the textfield still looses focus.
How can I fix this?
I recommend you to use the tableView:willDisplayCell:forRowAtIndexPath: method for adding the shadow.
Use the following code.
[textfield performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0];
Related
I have a custom UITableViewCell with objects in it (such as UILabel, UITextView, UITextField etc.). When a button gets selected, a cell gets added to the tableView.
When I run it on the simulator, and the cell gets added, all the visible cell's and subviews height get really compact. (I do have auto constraint applied.)
....
[[self myTableView] insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationTop];
If I do the following, the cells get back to normal:
NSArray* visibleCellIndex = self.myTableView.indexPathsForVisibleItems;
[self.myTableView reloadRowsAtIndexPaths:visibleCellIndex withRowAnimation:UITableViewRowAnimationFade];
[self.myTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:savedScrollPosition inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
The problem with reloading the visible cells, is: First, that's a workaround, not getting to the source of the problem. Second, it's not a fully functioning workaround, because the whole tableView scrolls all the way up for a second, then scroll back to position.
The reason why it was shrinking, is because, you have to implement the method of heightForRowAtIndexPath.
The only problem now, is that the tableView jumps up, then scrolls to position. Don't know why.
Does your target run only on iOS 8 and later? If yes, you can set self.tableView.rowHeight = UITableViewAutomaticDimension to enable Autolayout for your cells. Then, you also don't need to implement delegate tableView:heightForRowAtIndexPath:.
If you're already doing this, your problem probably lies in your custom cell. Maybe its constraints are not well defined? How do you initialize the cell's constraints?
Another idea is to trigger the layout pass manually in tableView:cellForRowAtIndexPath:. After the cell has been initialized and its text label values have been set, call:
[cell setNeedsLayout];
[cell layoutIfNeeded];
I have a UItableview with custom cells in it. The height of the cell changes when you select it and gives an expanding effect. However, when you you select the cell the background of all the subviews become transparent it seems. I've tried setting the cell's SelectedBackgroundView but that doesn't really affect the cells subviews.
Here are some images:
Closed:
Open:
![enter image description here][2]
This is how its supposed to look or at least does in XCode - (Sorry for the bad graphic here)
Call [tableView deselectRowAtIndexPath:indexPath animated:YES]; at didSelectRowAtIndexPath. This should solve your issue.
Edit
If you don't want to see any grey selection at all, then, in your cellForRowAtIndexPath, set the cell.selectionStyle to UITableViewCellSelectionStyleNone, like so:
cell.selectionStyle = UITableViewCellSelectionStyleNone;
Presuming that you have subclassed UITableViewCell for your custom cells, you can modify a cell's appearance when selected/deselected by overriding the setSelected method in your custom subclass. For example:
- (void) setSelected:(BOOL)selected {
[super setSelected:selected];
if (selected) {
//Configure the selected state here
} else {
//Configure the deselected state here
}
}
UITableViewCell changes the backgroundColor of all subviews on selection for some reason.
This might help:
DVColorLockView
If you want the normal selection behavior but want to exclude specific cell subviews, you can do something like this to lock the background color by subclassing and checking if it is locked in the backgroundColor setter.
It has been suggested that one scroll to the desired row in viewWillAppear, but this does not work with iOS 7. I have only been able to make this work in iOS 7 in the viewDidAppear callback. Unfortunately, you see the desired row scroll into view. I don't want to see any scrolling, I simply want the row to be visible when loaded. Can anyone suggest the proper way to do this in iOS 7?
It probably did not work in viewWillAppear, because that table had no data at this point.
Add [tableView reloadData];and it should work.
Let me get this straight: you want your table view to show a certain row at the top when the view apperas? Yes?
If so, you want:
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated
with your cell indexPath, UITableViewScrollPositionTop as scrollPosition and animated NO like so
[tableView scrollToRowAtIndexPath:myExampleindexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
If you know the cell index then it's as simple as:
[tableView setContentOffset:CGPointMake(cellLocation.x,cellLocation.y) animated:NO];
Call that just after you load your tableView data and it will scroll to your cell being on top. There are other options as well:
[tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:currentRow inSection:currentSection] animated:NO scrollPosition:UITableViewScrollPositionTop];
Use this code with whatever scrollPosition you would like and Apple takes care of the bounding to the table (whereas setting the scrolling position is all user defined, it could be out of the table's view).
EDIT:
You could surround your selecting code with a call to UIView setting no animations allowed. That has worked for me in the past with different things, but I have never tried it in viewDidLoad.
[UIView setAnimationsEnabled:NO];
//Scroll the tableview
[UIView setAnimationsEnabled:YES];
I've got a text field inside of a UICollectionViewCell that can receive first-responder status. The cell currently isn't visible on-screen, and I want to scroll to the cell based off of a button hit from a UISegmentedControl. There's two segments to this control… and a hit to the second segment should scroll to the first cell in the 2nd section of the UICollectionView. After this happens, the cell should get selected programatically, and then the text field inside of that cell is supposed to get first responder status and bring up the keyboard.
What's happening now (inside my action method from a value change from the segmented control) is that a call to -[UICollectionView selectItemAtIndexPath:animated:scrollPosition:] isn't scrolling to it at all (and I'm using UICollectionViewScrollPositionTop; may as well be "…None"). If I thumb down the list manually, the cell is indeed selected (it gets a darker background color in that state), but the text field certainly doesn't have first responder status.
To fix the scroll problem, I've been able to ascertain the position of the cell in the list, and scroll to the cell's content offset (I've also used scrollRectToVisible here). Then I manually select it (as well as telling the delegate to fire its appropriate method as well, where the cell's text field gains first responder status).
- (void)directionSegmentedControlChanged:(UISegmentedControl *)sender {
NSIndexPath *path = [NSIndexPath indexPathForItem:0 inSection:sender.selectedSegmentIndex];
UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:path];
[self.collectionView setContentOffset:attributes.frame.origin animated:YES];
[self.collectionView selectItemAtIndexPath:path animated:NO scrollPosition:UICollectionViewScrollPositionNone];
[self.collectionView.delegate collectionView:self.collectionView didSelectItemAtIndexPath:path];
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
BDKCollectionViewCell *cell = (BDKCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
[cell.textField becomeFirstResponder];
}
The problem here is that the cell as it's seen in -[collectionView:didSelectItemAtIndexPath:] is nil, because it's not in the visible cell set of the collection view when the method gets fired.
What's the best way to solve this? I've tried tossing my scrolling code inside of a [UIView animateWithDuration:animations:completion:] block, and assigned first responder upon completion there, but manually animating the collection view in this manner neglects to load any of the cells that should be scrolled past. Any ideas?
Update: many thanks to #Esker, who suggested I simply perform the "focus selection" action after a delay using Grand Central Dispatch. My solution ended up looking like this.
- (void)directionSegmentedControlChanged:(UISegmentedControl *)sender {
NSIndexPath *path = [NSIndexPath indexPathForItem:0 inSection:sender.selectedSegmentIndex];
UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:path];
[self.collectionView setContentOffset:attributes.frame.origin animated:YES];
dispatch_time_t startAfter = dispatch_time(DISPATCH_TIME_NOW, 0.28 * NSEC_PER_SEC);
dispatch_after(startAfter, dispatch_get_main_queue(), ^{
[self.collectionView selectItemAtIndexPath:path animated:NO scrollPosition:UICollectionViewScrollPositionNone];
[self collectionView:self.collectionView didSelectItemAtIndexPath:path];
});
}
I had a similar challenge with a UITableView: scrolling to a cell that was not yet visible, and assigning first responder to a UITextField within the target cell once it was visible. Here's a simplified description of how I handle this. I imagine this approach could work with a UICollectionView, but I don't have much experience with collection views.
If the desired cell/text field is currently visible, immediately send it becomeFirstResponder, and scroll to the cell if desired.
Otherwise, set a property in your view controller or a similar class that indicates that a text field needs focus, and which one needs focus
Tell the table view/collection view to scroll to the desired index path
In collectionView:cellForItemAtIndexPath:, you could try to check that property to see if a text field at the given indexPath needs to get focus, and if so, send it becomeFirstResponder immediately, but I found this won't work if the cell is scrolling into view, presumably because at this point, when you're configuring the new cell, it's not yet actually in the view hierarchy. So I added a check, if becomeFirstResponder returns NO at this point, I try again after a delay:
dispatch_after(someDelay, dispatch_get_main_queue(), ^(void){
[self getFocus:textField];
});
The getFocus method will both send becomeFirstResponder to the text field and clear that property that tracks which text field needs focus.
My actual implementation is somewhat specialized for the view model associated with my table view, and encapsulated in a couple of classes and using some KVO, but I wanted to avoid that and focus on the minimum required logic in the description above.
every time I scroll up or down, and cell which has been drawn already gets redrawn because "cellForRowAtIndexPath" is called again when that cell comes in view.
Is there a way to not call "cellForRowAtIndexPath" for a cell that has already been called for.
That is once cellForRowAtIndexPath is called for a indexpath it should not call it again when the user scrolls.
I can set flags in a flag array and do it manually but is there a more direct way?
I have most definitely a design that is causing performance issues. I can't explain the design nor change it therefore I am asking.
If you do not let the "cellForRowAtIndexPath" method calling again, it may create performance issue,
But you can do that by assigning different cell identifier for each row as follows.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[NSString stringWithFormat:#"Cell%d%d", indexPath.row, indexPath.section]];
//TRY TO REMOVE ALL CONTENT BEFORE CREATING NEW.
//USE IN "cellForRowAtIndexPath" method
for(UIView *view in cell.contentView.subviews){
if ([view isKindOfClass:[UIView class]]) {
[view removeFromSuperview];
}
}