I am new to objective c, and i am very interested in learn new technologies.
I have a UITapGestureRecognizer on a label. I am using 2 label ,when i tap on label1 load some data Tableview and when I tap on label 2 i want to change the Tableview.
I didn't find any solution for my requirement. so anyone help me .
suggest me guy's how to change the TableView data when tap on UILabel.
You can do this like following way:
Way 1:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
..
..
..
// Here is your stuff for cell
UILabel *lbl1 = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 80, 40)];
[lbl1 setText:#"Label"];
[lbl1 setUserInteractionEnabled:YES];
[cell.contentView addSubview:lbl1];
UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapAction:)];
tap.tag = [NSIndexPath row];
[tap setNumberOfTapsRequired:1];
[lbl addGestureRecognizer:tap];
...
}
- (void)tapAction:(id)sender {
switch(((UITapGestureRecognizer *)sender).view.tag) {
case 0:
// code for first TableView
break;
case 1:
// code for second TableView
break;
....
}
}
Way 2:
in cellForRowAtIndexPath: method:
UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapAction:)];
tap.tag = [NSIndexPath row];
...
Handler method:
- (void)tapAction:(id)sender
{
switch(((UITapGestureRecognizer *)sender).tag)
{
case 0:
NSLog(#"Tap action 1");
break;
case 1:
NSLog(#"tap action 2");
break;
..... }
I assume you have setup the datasource/delegate methods for the tableview? Then change necessary things within these methods when the label is tapped and simply call
[self.tableView reloadData]
If you don't understand anything about what I'm saying then I advise you to first read some manuals about tableviews before starting asking questions on SO :)
Welcome to Stack Overflow!
This answer should help clear things up for you nicely and give you a stronger understanding of how to use table view's and buttons to do what you need.
FIRST
To Alladinian's comment, I highly recommend switching over to the UIButton object instead of using UILabel objects. This way your elements can still display your text (as you were with labels) but you are able to make use of the built-in target capabilities of the button class.
- (void)addTarget:(nullable id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;
SECOND
Regarding your question, again, simply make use of an already built-in method that is designed for this very situation:
- (void)reloadData;
This method is available by using / setting UITableViewDelegate and the UITableViewDataSource. Like such: here is an example of a generic UIViewController class that conforms (can use properly) the convenient UITableView delegates.
Header File (SomeClassController.h)
// SomeClassController.h
#import <UIKit/UIKit.h>
#interface SomeClassController : UIViewController <UITableViewDelegate, UITableViewDataSource>
// do anything here. properties, methods, etc :)
#property (nonatomic, ...) UITableView *tableView;
#end
Implementation File (SomeClassController.m)
// ...
- (void)viewDidLoad {
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
Now by using UIButton objects you can set a method that will handle the user touching the screen by performing a method. Like here:
- (void)viewDidLoad {
...
[someButton addTarget:self action:#selector(didTouchButton:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)didTouchButton:(__kindof UIButton *)button {
// Here you received the users touch event on the button, do what you like!
// As your question asked how to reload table data, simply add this line
// after following my other steps.
[self.tableView reloadData]; // Here the magic is done!
// This method will automatically reload the table view to display the
// most recently updated data you have given it.
}
Make sure you change your data before reloading, otherwise it may not change both visually or content-wise.
Happy coding!
Related
I'm trying to express the following scenario in ReactiveCocoa and MVVM.
There's a table view which shows a list of Bluetooth devices nearby
On row selection we start a process of connecting to the selected device and display an activity indicator as an accessoryView of the selected cell.
Now we have alternative endings:
When connected successfully we dismiss the table view controller and pass device handle to the parent view controller or rather parent view model.
When during connecting process user taps another table view cell then we cancel the previous process and start a new one with the selected device.
On error show a message.
I have a problem with ending no 2. I came up with RACCommand in my view model that triggers the process of connection. Then in didSelectRowAtIndexPath I execute that command.
ViewModel:
- (RACCommand *)selectionCommand {
if (!_selectionCommand) {
_selectionCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
return [self selectionSignal];
}];
}
return _selectionCommand;
}
- (RACSignal *)selectionSignal {
// not implemented for real
return [[[RACSignal return:#"ASDF"] delay:2.0] logAll];
}
ViewController:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[activityIndicatorView startAnimating];
cell.accessoryView = activityIndicatorView;
[[self.viewModel.selectionCommand execute:indexPath] subscribeCompleted:^{
[activityIndicatorView stopAnimating];
cell.accessoryView = nil;
}];
}
This shows and hides the activity view during the connection process but only when I wait for it to finish without tapping on other cells.
I ask for a guidance on how such behaviour could be completed. (It also feels like this isn't the right place to subscribe to the signal, right? Should it go to viewDidLoad?)
Apparently I asked the wrong question. It should say "How to cancel a RACCommand". The answer is: takeUntil: can do that (source: https://github.com/ReactiveCocoa/ReactiveCocoa/issues/1326).
So if I modify my command creation method to look like below everything starts to work like I expected. Now it cancels itself when it is used again. Notice that allowsConcurrentExecution must be set to YES to enable this behaviour, otherwise the signal will emit errors saying that RACCommand is currently not enabled.
- (RACCommand *)selectionCommand {
if (!_selectionCommand) {
#weakify(self);
_selectionCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
#strongify(self);
return [[self selectionSignal] takeUntil:self->_selectionCommand.executionSignals];
}];
_selectionCommand.allowsConcurrentExecution = YES;
}
return _selectionCommand;
}
I do this by attaching a block operation to a custom UITableViewCell sub class. I make my tableViewCells part of this subClass and then when I'm laying out my tableviewcells in the view controller, I call to exposed block header in the UITabbleViewCell subclass where it's exposed in this subclasses header file and attach a touch even to the block operation. The custom UITableViewCell needs a tapgesture recognizer and this will do the trick, well it will do the trick as long as in your UITableViewCell custom sub class you also expose the various elements of each blooth tooth tableview cell, that is, create customized setters and getters. This is the easiest way to do it and it takes about 15 lines of code and ZERO third party libraries.
header file:
#property (nonatomic, copy) void (^detailsBlock)();
implementation file:
_tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(cellTapped:)];
[_tapGesture setDelegate:self];
[_tapGesture setCancelsTouchesInView:FALSE];
[self addGestureRecognizer:_tapGesture];
- (void)cellTapped:(UITapGestureRecognizer*)sender
{
if ([self detailsBlock]) {
[self detailsBlock]();
}
}
making the block work for a tableview in the viewcontroller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"something" forIndexPath:indexPath];
[cell setDetailsBlock:^{
[self termsButtonPressed];
}];
return cell;
}
-(void)termsButtonPressed
{
//do work
}
I'm struggling with a problem I encountered while trying to create a custom UITableViewCell.
I subclassed UITableViewCell in SGTableViewCell and added it in the prototype cell in the storyboard.
As you can see the label is connected
and the cell identifier is set correctly
Then I linked the label to the SGTableViewCell.h like this
#property (strong, nonatomic) IBOutlet UILabel *nameLabel;
and in the .m file I have this code
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[self addGestureRecognizer:recognizer];
self.nameLabel = [[UILabel alloc] init];
_checkView = [[UIView alloc] initWithFrame:CGRectNull];
_checkView.backgroundColor = kGreen;
_checkView.alpha = 0.0;
[self addSubview:_checkView];
self.nameLabel.text = #"Hello";
}
return self;
}
But when I use this cell in my tableview using this code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Episode *episode = [self.selectedSeason.episodeList objectAtIndex:indexPath.row];
SGTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Episode"];
UIView *selectionColor = [[UIView alloc] init];
selectionColor.backgroundColor = kSelectionGrey;
cell.selectedBackgroundView = selectionColor;
cell.backgroundColor = kBackgroundGrey;
cell.nameLabel.text = episode.name;
NSLog(#"%#", cell.nameLabel.text);
cell.nameLabel.textColor = [UIColor redColor];
return cell;
}
I get no text at all.
I tried logging the text from each label in each cell and it gives me the right text.
I tried setting programmatically a different disclosure indicator for the custom cell and it did change so everything is allocated and working but label is not displaying.
I honestly have no idea of what's the problem. Did I miss something?
Thank you
PARTIALLY SOLVED:
OK i tried doing the same thing on an empty project and everything worked flawlessly so I checked again my project and found this line
[self.tableView registerClass:[SGTableViewCell class] forCellReuseIdentifier:#"Episode"];
Seeing it was not necessary for the empty project i commented this line and everything started working.
The only problem i have now is that if i don't use this line i can't use the custom cell as was intended. In fact my custom cell is swipable using a pan gesture recognizer but without registering my custom class to the tableview seems like the swipe doesn't work.
Sorry for the trouble, seems like i messed up again :/
You shouldn't alloc init a label that you created in the storyboard, it is already allocated automatically. When you do self.nameLabel = [[UILabel alloc] init];, you reset the self.nameLabel property to point to a new empty memory location and not to the label created in the storyboard, hence you can change its text property and see the result in NSLog but not in the storyboard because it doesn't refer to that label in the storyboard.
Try removing all initialisation from the initWithStyle method (to make sure nothing is covering it such as that subview you create), and everything related to the label in the cellForRowAtIndexPath method (same reason), and try a simple assignment like self.nameLabel.text = #"Test text" in the cellForRowAtIndexPath method, it should work. Then add all your other initialisation.
And yeah, don't forget to input your cell reuse identifier "Episode" in the storyboard.
Make sure you:
Have linked the delegate and the datasource to the view the tablview is housed in.
Have that view implement UITableViewController and UITableViewDelegate (I'm pretty sure it is both of those).
Implement the necessary methods, which you seem to have done. You need the row size, section size, and the add cell methods
After updating the array linked to your tableview, call [tableView reloadData]
Have a look at this link:Tutorial to create a simple tableview app
after a lot of search not able to figure out my problem. i am new to iOS development,, so please don't mind if i am wrong…. :P :)
i have a button in my collection view cell , when button is pressed it should change its background image and get the contents of the cell on which cell's button is pressed.
i am doing it like this
in my .h file
#interface MyCell : UICollectionViewCell
#property (nonatomic, strong) UIButton *button;
#end
#interface DetailInvoicing : UIViewController
in .m file
#implementation MyCell
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
self.button = [UIButton buttonWithType:UIButtonTypeCustom];
self.button.frame = CGRectMake(175, 1, 50, 30);
self.button.backgroundColor = [UIColor clearColor];
[self.button addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.button setBackgroundImage:[UIImage imageNamed:#"buy.png"]forState:UIControlStateNormal];
[self.contentView addSubview:self.button];
}
return self;
}
- (void)buttonClicked:(UIButton *)sender
{
NSLog(#"button clicked!");
self.button.backgroundColor = [UIColor lightGrayColor];
[self.button setTitle:#"Sold" forState:UIControlStateNormal];
[self.button setBackgroundImage:[UIImage imageNamed:nil]forState:UIControlStateNormal];
}
#end
in viewdidload method
[self CollectionLoad];
and the CollectionLoad method is
// Use sqlite query to fetch data and save it in array, then
myCollection.delegate = self;
myCollection.dataSource = self;
[myCollection reloadData];
myCollection.backgroundColor=[UIColor clearColor];
[myCollection registerClass:[MyCell class] forCellWithReuseIdentifier:#"CellID"];
then
use datasource and delegate methods
on taping the button of desired cell button BG image is changed. and there is also some other random cell's button Background image is changed…
what is the problem here and second thing is
how to get the contents of the cell on this button tap....
Your problems are reuse and data management.
First, you are reusing cells by dequeuing them from a pool. You then add a new button with default configuration. So, each time the cell is reused you will add a new button (even if one is already there) and you won't give it any special configuration (part of your data management issue).
For data management, just changing the status of a button isn't enough - you also need to update your data model with the new status represented by the button selection. Then, next time you display that status you can set it to the correct value.
The general approach you should take is to subclass UICollectionViewCell so that you can add your button (only once). The cell subclass should be the target of the button (not the controller). The cell subclass could also have an #property which is the part of the data model associated with it so that it can update the contents when the button is tapped. And, when the property is set the cell can update itself based on the contents (set the appropriate button configuration). Alternatively, the cell could callback to the controller with the appropriate information (and the controller could configure the button in the data source methods).
So, I've added a UITableViewController and UITableViewCell into a UIViewController and while the cellForRowAtIndexPath delegate method works, didSelectRowAtIndexPath does not. Does anyone have any ideas?
EDIT 2: The delegate for the UITableView is set to the UIViewController.
EDIT 3: I found the answer to my issue on another Stack question here. Basically I had a UITap... on my [self view] that was blocking the didSelectRow.... I have no idea why the tap blocks the delegate method and I also have no idea how to get both the tap and the table working together simultaneously.
EDIT: The part that bugs me is that I've gotten this exact setup working on an earlier app. So that means I've missed a step somewhere along the way. The thing is, I've combed over all the steps and have compared previous app vs current app and I really have no idea what I missed.
I've added logging to both delegate methods and while one outputs, the other does not.
ViewController.h
#import "...TableViewCell.h"
...
UITableViewDataSource,
UITableViewDelegate
...
ViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"cellForRowAtIndexPath");
...
return Cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"didSelectRowAtIndexPath");
}
...TableViewCell.h (contents not important)
...TableViewCell.m (contents not important)
I found the answer on another StackOverflow question.
I had a UITapGestureRecognizer added to [self view] which I commented out, and then the delegate method worked.
Can anyone please tell me why this worked and also how I can get the UITapGestureRecognizer working on the same screen as the UITableView?
// Hide keyboard when user taps outside of it
UITapGestureRecognizer *tapGestureRecognizer =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(hideKeyboardOnTap)];
//[[self view] addGestureRecognizer:tapGestureRecognizer];
EDIT: Corrected typo of UITapeGestureRecognizer to UITapGestureRecognizer
have you set the delegate of the tableView?
myTableView.delegate = self;
EDIT: My bad, did not read that cell for row is being called.
You say that you have used a custom tableViewController. If you have overridden the didSelectRowAtIndexPath method, it might be important to call [super didSelectRowAtIndexPath:] in the tableViewController
EDIT 2: One more thing. I do not know the reason for this, but I faced the same issue some time back in a viewController. I resolved it by adding an empty implementation of didDeselectRowAtIndexPathin the same viewController. Try adding it to your table's delegate controller.
To answer to the question "how I can get the UITapGestureRecognizer working on the same screen as the UITableView";
You should "inform" your GestureRecognizer that other Recognizer can handle the same gesture:
You do that by implementing the method of UIGestureRecognizerDelegate
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
This is a small exemple...
#interface MyController : UIViewController<UIGestureRecognizerDelegate>
{
}
#end
-(void)viewDidLoad
{
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(actionOnTapGesture:)];
[gr setNumberOfTapsRequired:1];
[gr setDelegate:self];
[self.view addGestureRecognizer:gr];
}
-(BOOL) gestureRecognizer:(UIGestureRecognizer *) gestureRecognizer shouldRecognizeSimultaneouslyGestureRecognizer:(UIGestureRecognizer *) otherGestureRecognizer
{
return YES;
}
Check in the interface builder if the property selection is set to 'No selection'.
Change it to 'Single selection' or other option according to your needs.
This might be the reason why didSelect is not getting triggered.
In my tableview, I have several different custom cells. In one of them, it has a button. This button brings up another view controller. However, It is not needed until the tableview has fully loaded. In cellForRowAtIndexPath I set up all my different custom cells. I can uncomment [buttonCell.myButton setHidden:YES]; and it will hide my button. See below.
else if (indexPath.section == 3)
{
ButtonCell *buttonCell = [tableView dequeueReusableCellWithIdentifier:#"ButtonCell"];
//[buttonCell.myButton setHidden:YES];
cell = buttonCell;
}
return cell;
However, I want to then unhide the button after the tableview loads. I finish loading all my arrays in another method where I call reloadData. In that method, I tried to unhide the button by doing this..
[ButtonCell.myButton setHidden:NO];
But the compiler gives me a warning that property myButton is not found in ButtonCell. Does anyone have any ideas how to go about unhiding my button. What am I doing wrong, and what do I not get! Thanks for all your help.
EDIT 1
My button cell class is...
.h
#import
#interface ButtonCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UIButton *myButton;
- (IBAction)YDI:(id)sender;
#end
.m
#import "ButtonCell.h"
#import "AnotherWebViewController.h"
#implementation ButtonCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (IBAction)YDI:(id)sender
{
}
#end
EDIT 2
With everyone's help that answered (thank you all) I have gotten a bit further, but the button is not showing itself. So I still hide the button in cellForRowAtIndexPath, that works as should. Then in my method that I reload the data in I put the following code.
NSIndexPath *index = [NSIndexPath indexPathForRow:0 inSection:3];
ButtonCell *buttonCell = (ButtonCell *) [self.tableView cellForRowAtIndexPath:index];
[buttonCell.myButton setHidden:NO];
The ButtonCell with the button is always the fourth section (counting the first as 0) and it only has one row. Any other help would be appreciated. Almost there!
EDIT 3
Got it! However, it was due to a comment that I was able to figure it out. Thanks to #A-Live. Although I do know how to get the cell in a method outside of cellForRowAtIndexPath thanks to ElJay. So I am giving him the check since I learned something new which is why we post questions anyway. So inside my method cellForRowAtIndexPath is where I hide/show the button. I have a BOOL in my App called finished, it is originally set to true. When the table view ends loading it is set to false. So I just used this bool to show/hide the button.
else if (indexPath.section == 3)
{
ButtonCell *buttonCell = [tableView dequeueReusableCellWithIdentifier:#"ButtonCell"];
if (!_finished)
{
[buttonCell.myButton setHidden:YES];
}else{
[buttonCell.myButton setHidden:NO];
}
cell = buttonCell;
}
return cell;
Once again this is only part of my cellForRowAtIndexPath method. Thanks once again for all the help. I was surprised to see so many answers! Thanks.
Make the property publicaly accessible.
#property (nonatomic, retain) UIButton *myButton;
Then in cellForRowAtIndexpath
ButtonCell *buttonCell =(ButtonCell *) [tableView dequeueReusableCellWithIdentifier:#"ButtonCell"];
myButton belongs to a cell. You will need to get an instance of that UITableViewCell and then you can unhide it, this assumes you want to modify the cell's objects outside of cellForRowAtIndexPsth or willDisplayCell.
In your code
[ButtonCell.myButton setHidden:NO];
You are trying to use the object class name instead of the object name. You need to get the cell that contains your button
buttonCell = [tableView cellForRowAtIndexPath:indexPath];
buttonCell.myButton.hidden = NO;
Mistake in uppercase maybe ?
[buttonCell.myButton setHidden:NO]; // Trying to access instance variable
Instead of :
[ButtonCell.myButton setHidden:NO]; // Trying to access class variable
Do you have a public accessor for that property in the header file of ButtonCell? Something like #property (nonatomic, retain) UIButton *myButton;
This is how I usually see such a compiler warning.