I have been looking at trying to differentiate between editing states in my UITableView.
I need to call a method only when in editing mode after tapping the edit button, so when you get your cell slide in and you see the little circular delete icons but NOT when the user swipes to delete.
Is there anyway I can differentiate between the two?
Thanks.
EDIT:
Solution thanks to Rodrigo
Both each cell and the entire tableview has an 'editing' BOOL value, so I loop through all the cells and if more than one of them is editing then we know the whole table is (the user tapped the edit button), however if only one is editing then we know that the user has swiped a cell, editing that individual one, this lets me deal with each editing state individually!
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
int i = 0;
//When editing loop through cells and hide status image so it doesn't block delete controls. Fade back in when done editing.
for (customGuestCell *cell in self.tableView.visibleCells)
{
if (cell.isEditing) {
i += 1;
}
}
if (i > 1)
{
for (customGuestCell *cell in self.tableView.visibleCells)
{
if (editing)
{
// loop through the visible cells and animate their imageViews
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.4];
cell.statusImg.alpha = 0;
[UIView commitAnimations];
}
}
}
else if (!editing)
{
for (customGuestCell *cell in self.tableView.visibleCells)
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.4];
cell.statusImg.alpha = 1.0;
[UIView commitAnimations];
}
}
}
Even if this post is quite old, the following might be helpful to others:
If you implement the following delegate messages:
- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath;
and
- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath;
These methods will be called when editing a single line. -[UIViewController setEditing:animated:] will then only get called when the user hits the edit button.
There are one strategy, I do not test now, but maybe work.
You can set the UITableView to be in editing mode and test with isEditing function. But the cell have the same isEditing. So you can check if only one cell is in editing state or all the UITableView.
Check if when you set one cell to be in editing state, the UITableView change to editing state at all.
The solution I chose was to override the action for the edit button to a custom method, say editBtnTapped. In this method I set a variable editButtonPressed, and then, because we overrode the the edit button's action, manually call setEditing:animated:.
In viewDidLoad:
[self.navigationItem.rightBarButtonItem setAction:#selector(editBtnPressed)];
And then the new action method:
- (IBAction) editBtnPressed
{
if ([self isEditing])
{
self.editButtonPressed = NO;
[self setEditing:NO animated:YES];
}
else
{
self.editButtonPressed = YES;
[self setEditing:YES animated:YES];
}
}
Now in setEditing:animated: I check the editButtonPressed flag to determine if I'm in there because of an Edit button press or a simple user swipe. If I'm there due to the Edit button, I add the cell; otherwise I don't.
Keep in mind you may need the flag elsewhere, (e.g. numberOfRowsInSection).
Hope this alternative helps.
Regards,
--John
The only reliable way I have found to do this is to maintain a private flag inEditMode and toggle this flag in setEditing:animated. Then use inEditMode rather than isEditing to check if the table is in edit mode or not.
Related
I have this search bar in my app Please see the play, next and previous buttons.
When I search and click on a song from the search items resigning first responder for search bar hides my buttons i.e play, next and previous buttons.
I am using this code
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MPMediaItem * song;
_nowPlayingIndex=indexPath.row;
if (tableView == self.searchDisplayController.searchResultsTableView)
song = searchResults[_nowPlayingIndex];
else
song = _songs[_nowPlayingIndex];
[self.musicPlayer setNowPlayingItem:song];
[self.musicPlayer play];
_playButton.hidden=true;
_pauseButton.hidden=false;
[_mySearchBar resignFirstResponder];
}
Why are my buttons hidden?
Step 1 : What you can do is make a different function for showing and hiding these buttons or any other item.
Step 2 : You can then call these functions wherever required. e.g. Everytime after you have resigned the responder.
Step 3 : You can try setting the table view frame to some less height from code. And then test this thing. Because I think the height of your table view leads to the hiding of those buttons.
e.g. [self.tableView setFrame: CGRectMake(self.view.frame.origin.x, self.searchSong.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height - "button.height" - "navbar.height")];
Step 4 : Also you should debug your code as to when you execute the resignFirstResponder... please check what is the frame of your buttons. This way you will get to know their position.
Its just another way of solving your issue. If you still have questions, you can ask. I hope it works. :)
This is what i was looking for
- (void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView{
CGRect tableFrame= self.searchDisplayController.searchResultsTableView.frame;
tableFrame.size.height=200;
tableFrame.size.width=300;
[self.searchDisplayController.searchResultsTableView setFrame:tableFrame];
[self.searchDisplayController.searchResultsTableView reloadData];
}
and this [[myButton superview] bringSubviewToFront:myButton]; for bringing them to front.
when you search anything apply code to your buttons is search delegate
[grayBar bringSubViewToFront];
or
[yourButton1 bringSubViewToFront];
[yourButton2 bringSubViewToFront];
[yourButton3 bringSubViewToFront];
hope this helps
I have an UITableView with rows and sections.
I would like to scroll to the first item of the second section, letting the header of the first section visible. Like if I had manually scrolled the list until reaching that state.
---- TOP OF SCREEN ----
Header of first section
Header of the second section
cell 1
cell 2
cell 3
Header of the third section
cell 1
cell 2
...
scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]
does not do the job, it hides the header of the first section.
We're moving on. I found this method based on Kevin's idea. To be able to set animated to YES, I catch the end of animation using a delegate method of UIScrollView. It works. But any solution that would help not doing 2 animations would be greatly appreciated. Any idea about how to do this ?
- (IBAction) scrollToToday:(BOOL)animate {
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:animate];
if (animate == NO) [self showFirstHeaderLine:NO];
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
[self showFirstHeaderLine:YES];
}
- (void) showFirstHeaderLine:(BOOL)animate {
CGRect headerRect = [self.tableView rectForHeaderInSection:1];
CGPoint scrollPoint = headerRect.origin;
scrollPoint.y -= headerRect.size.height;
[self.tableView setContentOffset:scrollPoint animated:animate];
}
Dude to this code, the process when animated is set to YES should loop infinitely beetween scrollViewDidEndScrollingAnimation and showFirstHeaderLine... It loops, yes, but only once... Any idea about why ?
You can grab the rect for the row you want, then subtract the height of the header of the previous section and scroll to that point. Something like the following (untested) should work:
CGRect rowRect = [table rectForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]];
CGRect headerRect = [table rectForHeaderInSection:0];
rowRect.origin.y -= headerRect.size.height;
rowRect.size.height += headerRect.size.height;
[table scrollRectToVisible:rowRect animated:YES]; // UITableView is a subclass of UIScrollView
I tried your code, and it works!!
For the loop question, since you are setting a offset(SetContentOffset), it has nothing to do with the scroll. It is will not call scrollView delegate. SO the scrollViewDidEndScrollingAnimation will be called only once, which has been called from scrollToRowAtIndexPath.
I'm using a TableView to show, edit and write Data to a file...when the editing modes end I write them to the file and afterwards I want to clear the inputFields of the tableCells.
I'm doing this with:
- (void) setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
if (editing == NO) {
//Write to file
//Clear Data
[self.tableView reloadData];
}
}
but when i reload the Data the animation isn't shown.
What way can i reload the Data and still get an animation?
Why not just clear the input fields in the existing cells instead of calling reloadData? Iterate through all of the UITableViewCells in self.visibleCells and clear the input field in each of them in turn - that should leave the animation uninterrupted.
When a hardware keyboard is used with iOS, pressing tab or shift-tab automatically navigates to the next or previous logical responder, respectively. Is there a way to do the same programmatically (i.e. simulating the tab key rather than keeping track of the logical order manually)?
As William Niu is right but you can also use this code explained below.
I have used this and got success.Now consider the example of UITextField...
You can use UITextView's delegate method -(BOOL)textFieldShouldReturn:(UITextField*)textField as explained below.
But before doing this you should have to give tag to each UITextField in an Increment order...(Increment order is not required necessary ,but as for my code it is required, you can also use decrement order but some code changes for doing this)
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
NSInteger nextTag = textField.tag + 1;
UIResponder* nextResponder = [self.view viewWithTag:nextTag];
if (nextResponder) {
[nextResponder becomeFirstResponder];
} else {
[textField resignFirstResponder];
}
return YES;
}
Hope this will work for you...
Happy coding....
You may define the "tab-order" using the tag property. The following post describes how to find the next tag index to go to for UITextFields,
How to navigate through textfields (Next / Done Buttons).
Here is a modified version of the code from that post. Instead of removing keyboard at the last tag index, this following code would try to loop back to the first tag index.
-(BOOL)textFieldShouldReturn:(UITextField*)textField;
{
NSInteger nextTag = textField.tag + 1;
// Try to find next responder
UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
if (nextResponder) {
// Found next responder, so set it.
[nextResponder becomeFirstResponder];
return NO;
}
// Try to find the first responder instead...
// Assuming the first tag index is 1
UIResponder* firstResponder = [textField.superview viewWithTag:1];
if (firstResponder) {
// loop back to the first responder
[firstResponder becomeFirstResponder];
} else {
// Not found, so remove keyboard.
[textField resignFirstResponder];
}
return NO; // We do not want UITextField to insert line-breaks.
}
If you want an UI element other than UITextField, you should still be able to use the same logic, with a few more checks.
Not sure if this helps, but in the context of a UITextFields, if you implement UITextFieldDelegate, - (BOOL)textFieldShouldReturn:(UITextField *)textField will get called when the return key of the soft keyboard is pressed.
I've tried to hit directly on my laptop keyboard and it seemed to jump between all the textfields in the order in which you've added them to the view, but didn't go to any other types of fields (Buttons etc.).
key on the keyboard is simulating the key on the soft keyboard of the simulator, which works as expected.
I've got a UISearchBar in my interface and I want to customise the behaviour of the the small clear button that appears in the search bar after some text has been entered (it's a small grey circle with a cross in it, appears on the right side of the search field).
Basically, I want it to not only clear the text of the search bar (which is the default implementation) but to also clear some other stuff from my interface, but calling one of my own methods.
I can't find anything in the docs for the UISearchBar class or the UISearchBarDelegate protocol - it doesn't look like you can directly get access to this behaviour.
The one thing I did note was that the docs explained that the delegate method:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText;
is called after the clear button is tapped.
I initially wrote some code in that method that checked the search bar's text property, and if it was empty, then it had been cleared and to do all my other stuff.
Two problems which this though:
Firstly, for some reason I cannot fathom, even though I tell the search bar to resignFirstResponder at the end of my method, something, somewhere is setting it back to becomeFirstResponder. Really annoying...
Secondly, if the user doesn't use the clear button, and simply deletes the text in the bar using the delete button on the keyboard, this method is fired off and their search results go away. Not good.
Any advice or pointers in the right direction would be great!
Thanks!
Found the better solution for this problem :)
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
if ([searchText length] == 0) {
[self performSelector:#selector(hideKeyboardWithSearchBar:) withObject:searchBar afterDelay:0];
}
}
- (void)hideKeyboardWithSearchBar:(UISearchBar *)searchBar{
[searchBar resignFirstResponder];
}
The answer which was accepted is incorrect. This can be done, I just figured it out and posted it in another question:
UISearchbar clearButton forces the keyboard to appear
Best
I've got this code in my app. Difference is that I don't support 'live search', but instead start searching when the user touches the search button on the keyboard:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
if ([searchBar.text isEqualToString:#""]) {
//Clear stuff here
}
}
Swift version handling close keyboard on clear button click :
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.characters.count == 0 {
performSelector("hideKeyboardWithSearchBar:", withObject:searchBar, afterDelay:0)
}
}
func hideKeyboardWithSearchBar(bar:UISearchBar) {
bar.resignFirstResponder()
}
You could try this:
- (void)viewDidLoad
{
[super viewDidLoad];
for (UIView *view in searchBar.subviews){
for (UITextField *tf in view.subviews) {
if ([tf isKindOfClass: [UITextField class]]) {
tf.delegate = self;
break;
}
}
}
}
- (BOOL)textFieldShouldClear:(UITextField *)textField {
// your code
return YES;
}
I would suggest using the rightView and rightViewMode methods of UITextField to create your own clear button that uses the same image. I'm assuming of course that UISearchBar will let you access the UITextField within it. I think it will.
Be aware of this from the iPhone OS Reference Library:
If an overlay view overlaps the clear button, however, the clear button always takes precedence in receiving events. By default, the right overlay view does overlap the clear button.
So you'll probably also need to disable the original clear button.
Since this comes up first, and far as I can see the question wasn't really adequately addressed, I thought I'd post my solution.
1) You need to get a reference to the textField inside the searchBar
2) You need to catch that textField's clear when it fires.
This is pretty simple. Here's one way.
a) Make sure you make your class a , since you will be using the delegate method of the textField inside the searchBar.
b) Also, connect your searchBar to an Outlet in your class. I just called mine searchBar.
c) from viewDidLoad you want to get ahold of the textField inside the searchBar. I did it like this.
UITextField *textField = [self.searchBar valueForKey:#"_searchField"];
if (textField) {
textField.delegate = self;
textField.tag = 1000;
}
Notice, I assigned a tag to that textField so that I can grab it again, and I made it a textField delegate. You could have created a property and assigned this textField to that property to grab it later, but I used a tag.
From here you just need to call the delegate method:
-(BOOL)textFieldShouldClear:(UITextField *)textField {
if (textField.tag == 1000) {
// do something
return YES;
}
return NO;
}
That's it. Since you are referring to a private valueForKey I can't guarantee that it will not get you into trouble.
Best solution from my experience is just to put a UIButton (with clear background and no text) above the system clear button and than connect an IBAction
- (IBAction)searchCancelButtonPressed:(id)sender {
[self.searchBar resignFirstResponder];
self.searchBar.text = #"";
// some of my stuff
self.model.fastSearchText = nil;
[self.model fetchData];
[self reloadTableViewAnimated:NO];
}
Wasn't able to find a solution here that didn't use a private API or wasn't upgrade proof incase Apple changes the view structure of the UISearchBar. Here is what I wrote that works:
- (void)viewDidLoad {
[super viewDidLoad];
UITextField* textfield = [self findTextFieldInside:self.searchBar];
[textfield setDelegate:self];
}
- (UITextField*)findTextFieldInside:(id)mainView {
for (id view in [mainView subviews]) {
if ([view isKindOfClass:[UITextField class]]) {
return view;
}
id subview = [self findTextFieldInside:view];
if (subview != nil) {
return subview;
}
}
return nil;
}
Then implement the UITextFieldDelegate protocol into your class and overwrite the textFieldShouldClear: method.
- (BOOL)textFieldShouldClear:(UITextField*)textField {
// Put your code in here.
return YES;
}
Edit: Setting the delegate on the textfield of a search bar in iOS8 will produce a crash. However it looks like the searchBar:textDidChange: method will get called on iOS8 on clear.