I currently have a table with 8 rows that each have a label on the right side and a button on the left. I was hoping that I could have all the buttons hidden until the user presses an "edit" button in the top right corner and then they would appear allowing the user to interact with each table cell. I don't know if this is possible, because they are in UITableViewCells or if there is an easier method to summoning a button for each cell
UPDATE
okay so I have placed in all the hidden properties and there seem to be no errors, but the app doesn't recognize any of it. The buttons remains unhidden despite the fact that they are set to be initially hidden. Here is my code
Here is my Table Cell code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"BlockCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = #"Free Block";
UIButton*BlockButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
BlockButton.frame = CGRectMake(225.0f, 5.0f, 75.0f, 35.0f);
[BlockButton setTitle:#"Change" forState:UIControlStateNormal];
[BlockButton addTarget:self action:#selector(Switch:) forControlEvents:UIControlEventTouchUpInside];
Blockbutton.backgroundColor = [UIColor colorWithRed:102/255.f
green:0/255.f
blue:51/255.f
alpha:255/255.f];
Blockbutton.hidden = YES;
[cell addSubview:BlockButton];
return cell;
}
and here is my method code:
- (IBAction)Editmode:(UIButton *)sender
{
Blockbutton.hidden = !Blockbutton.hidden;
[self.tableView reloadData];
}
any thoughts or ideas as to what might be the issue?
You'll need to create a UITableViewCell subclass if you don't already have one. In that class, override setEditing:animated: and if the new value is YES, then enable/add/unhide the button.
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
if (editing) {
// add your button
someButton.hidden = NO;
} else {
// remove your button
someButton.hidden = YES;
}
}
It would be optional, but you are encouraged to animate the change if animated is YES.
Note: this assumes you have the edit button already hooked up the change the editing mode of the UITableView. If you don't, call setEditing:animated: on the UITableView in the button action. This will automatically call setEditing:animated: on each visible table cell.
The trick here is to keep in mind that a table's cells are determined by cellForRowAtIndexPath:. You can cause that method to be called all over again by sending the table reloadData:.
So, just keep a BOOL instance variable / property. Use the button to toggle that instance variable and to call reloadData:. If, at the time cellForRowAtIndexPath: is called, the instance variable is YES, set the button's hidden to YES; if NO, to NO.
take a BOOL variable which defines the whether to show delete button or not, use this BOOL var to for btnName.hidden = boolVar, initially make boolVar = NO, when user taps on edit toggle bool var and reload the tableview.
Another option is to test if you are in edit mode in the cellForRowAtIndexPath method.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = //(obtain your cell however you like)
UIButton *button = cell.button; //(get button from cell using a property, a tag, etc.)
BOOL isEditing = self.editing //(obtain the state however you like)
button.hidden = !isEditing;
return cell;
}
And whenever you enter editing mode, reload tableView data. This will make the table view ask for the cells again, but in this case the buttons will be set not to hide.
- (void)enterEditingMode {
self.editing = YES;
[self.tableView reloadData];
}
Related
I want to know that how to get selected cell's row index on selection of cell. But how to do it without usingUITableViewDelegate methods.
Please tell me.
I have search lot but not getting solution so please if anyone know tell me.
Please shared the viewpoints regarding it.
Thanks in Advance...!!!
In that case, your interviewer wanted to know how can you implement the delegation yourself...
To achieve that, Create a custom class "YourTableViewCell" extended fromUITableViewCell and use this class object to be returned in -tableView:cellForRowAtIndexPath:
Write a protocol "CellSelectionProtocol" with a method -
-(void) cellSelected: (YourTableViewCell *) cell;
Now delegate this protocol to your ViewController that has your TableView
and define the implementation of the method as below -
-(void) cellSelected: (YourTableViewCell *) cell
{
NSIndexPath *selectedIndexPath = [_yourTableView indexPathForCell: cell];
}
My answer would be this if it was an interview, and I am pretty sure it would be accepted.
But for a good architecture... the protocol & delegate implementation should be in two levels, like ->
YourTableViewCell -> delegates -cellSelected: -> YourTableView -> delegates -tableView:didSelectRowAtIndexPath: -> YourViewController
Please see: Your interviewer just wanted to know how you create delegations manually, instead of using default UITableViewDelegates.
EDIT # Unheilig
I mean in 2 levels because, the selection of a UITableViewCell has to be delegated to the UITableView and not directly to the controller for the following reasons
UITableViewCell is subview of UITableView
There can be multiple UITableView in a controller. So if you delegate cell selection directly, how will you tell the controller that cell got selected for which UITableView object?
Also UITableView might have to do other processing with other UITableViewCell, Like if selected and changes backgroundColor, the previous selected should get deselected and get default backgroundColor. Or add to the array of selected cells, if multiple selection is enabled.
There are many such similar architectural necessities that make me say - "But for a good architecture... the protocol & delegate implementation should be in two levels, like ->"
I hope that is pretty explanative now...
There is no way to get selected cell row index with out using tableview delegate methods.
when you click on tableview, didSelectRowAtIndexPath called and get the tableview cell index.
There is one way to do this, but it is not correct procedure to get tableview cell index. Create a button on tableviewcell and pass the indexvalue as sender tag to button action. But need to click only on that button.
Answer edited:
Create a transparent button on tableview cell in cellForRowAtIndexPath method and pass the cell index to button tag.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
}
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, 0, cell.frame.size.width, cell.frame.size.height);
button.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.0];
[button addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
button.tag = indexPath.row;
[cell addSubview:button];
cell.textLabel.text = [NSString stringWithFormat:#"%#",[numberArray objectAtIndex:indexPath.row]];
return cell;
}
-(void)buttonClicked:(id)sender
{
NSLog(#"%ld",(long)[sender tag]);
}
I have the following situation:
I have a CollectionView whose cells each contain a button
in cellForItemAtIndexPath:, I set a string on each button depending on the cell's [indexPath row]
in the button's action method, I call reloadData: on the CollectionView (because I need to update the information represented by the button texts)
momentarily, while the button pressed is selected, the cells in the CollectionView are displayed "jumbled". (Once the button is released, everything returns to "normal" and the cells appear in the correct order: so ultimately, this is an aesthetic issue as far as the application is concerned.)
So specifically, we have code such as this:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = (MyCell *) [collectionView dequeueReusableCellWithReuseIdentifier: #"MyCellType" forIndexPath: indexPath];
NSString *str = [NSString stringWithFormat:#"%d", [indexPath row]];
[cell.answerButton setTitle: str forState:UIControlStateNormal];
// Set tag on button to mark which row no
// Set button background image depending on some internal data
return cell;
}
- (IBAction)answerButtonPressed:(id)sender {
UIButton *button = (UIButton *) sender;
// look at button tag, decide which button pressed, update internal
// data which might mean we need to change button colours
[_collectionView reloadData];
}
I tried putting the call to reloadData: in a dispatch_async to "delay" the update (I come from a Java background, and putting UI updates in a SwingUtilities.invokeLater() can sometimes help in cases such as this), but this didn't make a difference.
Can anyone assist: am I doing something wrong, or is this just a UI glitch that I have to live with?
instead of reloading the data for the entire collectionView, try pinpointing just the cell that needs updating.
- (IBAction)answerButtonPressed:(id)sender {
UIButton *button = (UIButton *) sender;
// look at button tag, decide which button pressed, update internal
// data which might mean we need to change button colours
NSInteger thisTag = button.tag;
NSIndexPath* thisIndexPath = [NSIndexPath indexPathForItem:thisTag inSection:0];
[_collectionView reloadItemsAtIndexPath:#[thisIndexPath]];
}
I am creating an app in where I need to add a bunch of sliders in a Dynamic UITableView. I added the slider like this:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
UISlider *slider = [[UISlider alloc]init];
//ALL OTHER CODE
[cell addSubview:slider];
return cell;
}
Now the slider is added to the UITableView but the if I changed the value of the first slider another slider changes with it.I know this is something to do with dequeing the cell but how do I fix it?
EDIT:
I tried #daveMack answer like this:
CustomCell.m:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
self.checkBox = [[M13Checkbox alloc]initWithTitle:#"Checkbox!"];
self.checkBox.checkAlignment = M13CheckboxAlignmentLeft;
[self addSubview:self.checkBox];
}
return self;
}
Cell For Row At Index Path:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CheckboxCell *cell;
cell = [self.tableView dequeueReusableCellWithIdentifier:#""];
if (!cell) {
cell = [[CheckboxCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
return cell;
}
return cell;
}
No, no, no! I would not suggest you add views like that to your UITableViewCell. Funky stuff can happen as you have experienced yourself because of the dequeueing process.
I suggest you do the following:
Create a custom table view cell with its appropriate .h, .m file and .xib file
In your custom cell you can add WHATEVER views you like and however many views that you like.
Make sure you create a property of type UIScrollView in your .h file and link it to the interface builder to your custom cell's slider, call the property slider.
Now in your main view controller where you are creating your table, make sure you have an NSMutableArray and name it something like sliderValuesArray that can store all your slider values for each cell. You want to make sure that the number of cells is equal to the number of elements in your sliderValuesArray.
Then you can do something like this in your cellForRowAtIndexPath delegate method:
In your cellForRowAtIndexPath method do something like this:
myCustomCell.slider.maximumValue = 100;
myCustomCell.slider.minimumValue = 1;
myCustomCell.slider.continuous = TRUE;
//set a method which will get called when a slider in a cell changes value
[myCustomCell.slider addTarget:self action:#selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
//Keep a reference to each slider by assigning a tag so that we can determine
//which slider is being changed
myCustomCell.slider.tag = indexPath.row;
//Grab the value from the sliderValuesArray and set the slider knob to that position
myCustomCell.slider.value = [[sliderValuesArray objectAtIndex:indexPath.row] intValue];
Now in your sliderChanged method you can do this:
-(void)sliderChanged:(UISlider)sender{
//Grab the slider value, it needs to be converted to an NSNumber so that we can
//store it successfully as an object in our sliderValuesArray
NSNumber sliderValue = [NSNumber numberWithInt:sender.value];
//This is how we determine which position in our slidersArray we want to update,
//Based on the tag we set our slider view on initialisation in our cellForRowAtIndexPath
int cellPosition = sender.tag;
//Use the cellPosition to update the correct number in our sliderValuesArray
//with the sliderValue retrieved from the slider that the user is sliding
[sliderValuesArray replaceObjectAtIndex:cellPosition withObject:sliderValue];
}
I ran into this same issue awhile back. The solution I came up with was to subclass UITableViewCell. Then I added the slider in the init method of the subclass and exposed it via a property.
Now, when you want to change the value of JUST ONE slider, you would do something like:
[cell slider]setValue:(someValue)];
I am creating one table view based application. I have created a custom table cell for table, that contains 2 labels, 1 image and 1 button. The table view Data source method is working properly. I am using xib for both custom cell and view controller class and i connect delegate and data source to the file's owner. But the problem is when i select the table row, didSelectRowAtIndexPath is not getting fire. As mentioned the only way to fire it is to hold down on the cell for about 3-4 seconds. Does anyone have any idea why this is happening?
Thanks for any pointers...
Here is my table view methods..
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [finalAddonsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
NewCustomCell *cell = (NewCustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib=[[NSBundle mainBundle]loadNibNamed:#"NewCustomCell" owner:self options:nil];
cell=[nib objectAtIndex:0];
}
Addons *addons1=[[Addons alloc]init];
addons1= [finalAddonsArray objectAtIndex:indexPath.row];
if (addons1.data == nil) {
cell.ivCategory.image = [UIImage imageNamed:#"blogo.jpg"];
}
else
{
cell.ivCategory.image=[UIImage imageWithData:addons1.data];
}
cell.lblTitle.text = addons1.name;
if (addons1.price == nil) {
cell.lblPrice.text = nil;
}
else{
cell.lblPrice.text = [NSString stringWithFormat:#"%# rs",addons1.price];
}
[cell.button addTarget:self
action:#selector(editButtonPressed:)
forControlEvents:UIControlEventTouchUpInside];
cell.button.tag=indexPath.row;
index = indexPath;
cell.selectionStyle = UITableViewCellSelectionStyleGray;
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"sjcjksbcjksbcfkebscf1234567890");
}
One more thing i am getting that if i am using default UITableViewCell instead of custom cell then also my problem is same, delegate method is not getting fire.
Custom cell properties:
same problem happened with me because I have added a tap gesture recogniser over it.
If you have used any gesture recognizer try removing it and check if it causing the problem.
EDIT: Solution as commented by the Ali:
If you have used tap gesture you can use [tap setCancelsTouchesInView:NO];
I was faced with a similar issue:
For me, the problem was because my UITableView was added to an UIScrollView and more specifically to its contentView.
It appears that inside the contentView, I had to stay press 2-3 sec to fire the didSelectRowAtIndexPath method.
I moved my TableView to self.view instead of contentView and it solved the problem!
Maybe you will call the method
[tableView deselectRowAtIndexPath:indexPath animated:NO];
before Push ViewController or Other Operation. Like
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// 1. manual call this method to deSelect Other Cell
[tableView deselectRowAtIndexPath:indexPath animated:NO];
// 2. than do other operation
PushViewController Or Some Animation ....
}
that`s solve my problem .
As others suggested, [tap setCancelsTouchesInView:NO]; does the trick.
However, I want to make one thing clear:
If you think that you did not implement tapgesture and are curious about why you had to add your view into the protected views, check out your class because most probably you have inherited some class and that class includes tap gesture recognizer in it.
In my case, I did the following:
- (NSMutableArray *)tapProtectedViews
{
NSMutableArray *views = [super tapProtectedViews];
[views addObject:self.mTableView];
return views;
}
Edit for Swift 4+
Assuming you have a UITapGestureRecognizer instance named tapGesture:
func disableTapGesture(){
tapGesture.cancelsTouchesInView = false
}
Or you can:
if self.view.gestureRecognizers?.isEmpty == false{
for recognizer in self.view.gestureRecognizers!{
self.view.removeGestureRecognizer(recognizer)
}
}
Dear i faced the same problem. When i tapped the cell but didselectrowatindexpath was not called than it was suddenly called when i released the button after pressing it for few seconds.
If you are facing the same issue there must be a
1. UITapGestureRecognizer that is creating problem for you
or
2. a scroll view in which you placed you table view.
Thus you should remove the gesture or the super scroll view in which your table view is placed
If you have custom gesture object on your view, check override func gestureRecognizerShouldBegin(_ gesture: UIGestureRecognizer) -> Bool delegate. Compare custom gesture with sender gesture, If its not custom gesture object, pass it to the the super. So system gestures/taps won't get blocked.
I'm not sure about this, but Delays Content Touches might have something to do with it.
I have a UITableView with a UITextField inside of each cell. A model object that stores the index of the cell that is currently being edited. If the cell scrolls off-screen, my app takes away first-responder status. (Failing to do so may cause problems). Now, suppose a cell (possibly the same one, or possibly a different one) corresponding to that index is about to scroll back onto the screen. I want to make that cell's textField the firstResponder. My delegate does receive a call
tableView: willDisplayCell: forRowAtIndexPath:
corresponding to the new cell. However, calling becomeFirstResponder: at that point does not help as the cell won't accept firstResponder status until it has been displayed.
Short of using a timer, any ideas for how to call becomeFirstResponder: at a point when the cell is in fact able to become the first responder?
EDIT: cellForRowAtIndexPath: is always called before willDisplayCell:. So no help there.
I haven't tried this, but the first thing I'd try is in cellForRowAtIndexPath...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// standard stuff to build cell and setup it's state
if ([indexPath isEqual:self.myModel.indexPathOfTextFieldBeingEdited]) {
// you probably have a handle to the text field from the setup above
UITextField *textField = (UITextField *)[cell viewWithTag:SOME_TAG];
[textField performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.0];
}
return cell;
}
You have to show cell on the screen to make it as first responder. Do at first:
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:NO];
and then call first responder on it's label/textField.
Here's what I did in MonoTouch - it's important that you do not animate the ScrollToRow() - i.e. "animated:NO" as shown in the answer by edzio27 (thanks edzio27 :) ).
var newCell = (UIOrderLineCell)tableView.CellAt(newIndexPath);
if (newCell == null)
{
tableView.ScrollToRow(newIndexPath, UITableViewScrollPosition.Middle, false);
newCell = (UIOrderLineCell)tableView.CellAt(newIndexPath);
}