I have a storyboard with a UINavigationViewController that holds a UITableView and a UIToolBar. The TableView populates well but doesn't respond when I touch a TableViewCell. It lits up but the selection doesn't persist and the handler function isn't called. When I make some sort of swipe over the cell it sometimes records the touch event and the handler is called. Looks like as if touchEvents aren't recognized or hold back. This is my setup:
The View component config of the NavigationView
The UITableView config
Remarkable is that if I touch the cell with 2 fingers it selects the cell...! So this handler gets called in that case:
- (void)viewDidLoad {
[super viewDidLoad];
self.selectedWerkzaamheden = self.model.selectedWerkorder.werkbonWerkzaamheden;
self.selectedWerkzaamheden = [NSMutableArray arrayWithArray:self.model.selectedWerkorder.werkbonWerkzaamheden];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(dismissKeyboard)];
[self.view addGestureRecognizer:tap];
[self.myTableView reloadData];
}
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.selectedWerkzaamheidIndex = indexPath.row;
self.selectedWerkzaamheid = self.selectedWerkzaamheden[self.selectedWerkzaamheidIndex];
self.trashBtn.enabled = TRUE;
}
The declaration file:
#import "LmxViewController.h"
#import "LmxDropDownDataVO.h"
#interface LmxWerkzaamheidViewController : LmxViewController <UITableViewDataSource>
#property NSString *faultTitle;
#property NSString *faultMessage;
#property IBOutlet UIButton *selectWerkzaamheidBtn;
#property LmxDropDownDataVO *selectedWerkzaamheid;
#property NSInteger selectedWerkzaamheidIndex;
#property NSMutableArray *selectedWerkzaamheden;
#property IBOutlet UIBarButtonItem *submitBtn;
#property IBOutlet UITableView *werkzaamheidTableView;
#property (strong, nonatomic) IBOutlet UIBarButtonItem *trashBtn;
- (IBAction)selectWerkzaamheid:(id)sender;
- (IBAction)saveWerkzaamheidWithWerkorder:(id)sender;
- (IBAction)cancel:(id)sender;
#end
Might there be something wrong in the way I supply the cells?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSMutableArray *werkzaamheden = self.selectedWerkzaamheden;
static NSString *simpleTableIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
NSInteger row = indexPath.row;
LmxDropDownDataVO *data = (LmxDropDownDataVO *)werkzaamheden[row];
cell.textLabel.text = data.naam;
return cell;
}
The problem was I did leave a piece of code in the viewDidLoad class that obscured the touch event of the table view cell.
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(dismissKeyboard)];
[self.view addGestureRecognizer:tap];
Apparently this is blocking the touch events to reach the Table View Delegate function:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Thx for your contributions.
Related
I have a viewController that contains a programmatically created tableView.
#property (strong, nonatomic) UITableView *numbersTableView;
//...
self.numbersTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, -100, self.view.frame.size.width-20, 50)];
self.numbersTableView.delegate = self;
self.numbersTableView.dataSource = self;
self.numbersTableView.tag = 1;
self.numbersTableView.layer.cornerRadius = 5;
[self.numbersTableView setBounces:false];
[self.numbersTableView setHidden:true];
[self.numbersTableView registerClass:[AZGCountryCell class] forCellReuseIdentifier:#"NumbersTableViewCell"];
[self.view addSubview:self.numbersTableView];
For the prototype cell I want to use a prototype that I created somewhere else in another viewController and I designed It in storyboard.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
if (tableView.tag == 1) { //tableView == self.numbersTableView
NSString *cellID = #"NumbersTableViewCell";
AZGCountryCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil) {
cell = [[AZGCountryCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
cell.countryName.text = self.numbersArray[indexPath.row].number;
cell.flagImageView.image = [UIImage imageNamed:self.numbersArray[indexPath.row].country];
return cell;
}
}
And my custom cell contains one UIImageView and one UILabel but when I run the code the cells are empty and I can see the UIImageView and UILabel are nil!
#import <UIKit/UIKit.h>
#interface AZGCountryCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UIImageView *flagImageView;
#property (strong, nonatomic) IBOutlet UILabel *countryName;
#property (strong, nonatomic) IBOutlet UILabel *countryCode;
#end
#import "AZGCountryCell.h"
#implementation AZGCountryCell
#synthesize flagImageView;
#synthesize countryName;
- (void)awakeFromNib {
[super awakeFromNib];
self.flagImageView.layer.cornerRadius = self.flagImageView.frame.size.width/2;
self.flagImageView.layer.masksToBounds = YES;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
#end
Then what should I do to have properly filled cells in my numbersTableView?
Unfortunately you can't reuse a table cell you've defined in a table view in a storyboard in your separate class.
In order for you to be able to share your cell between the different views, you'll have to extract the view to a separate nib and register that nib go be used on both tables.
Alternatively you can implement the cell's UI in code in the table view cell subclass.
You getting empty cell because custom tableview cell not registered, use
[self.numbersTableView registerNib:[UINib nibWithNibName:#"AZGCountryCell" bundle:nil] forCellReuseIdentifier:#"NumbersTableViewCell"];
instead of
[self.numbersTableView registerClass:[AZGCountryCell class] forCellReuseIdentifier:#"NumbersTableViewCell"];
I have implemented my own UITableViewCell and created a .xib for it. I have connected all the labels using a strong IBOutlet and initialized the UILabels in the awakeFromNib() method. However, whenever I run the iOS Simulator, I run into an issue where the UILabel is (null) in NSLog.
I am wondering if it has to do with how I am loading in the text for the UILabel. I have tried to create a shortened version of the project below that outlines the issue I'm running into.
I would also like to note that I can click on the actual rows, but that there is still no text displaying.
My code:
ToDoCell.h
#import <UIKit/UIKit.h>
#interface ToDoCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UILabel *minutesLeft;
#property (strong, nonatomic) IBOutlet UILabel *hoursLeft;
#property (strong, nonatomic) IBOutlet UILabel *daysLeft;
#property (strong, nonatomic) IBOutlet UILabel *taskLabel;
#end
ToDoCell.m
#import "ToDoCell.h"
#implementation ToDoCell
#end
ToDoViewController.m
#import "ToDoViewController.h"
#import "ToDoCell.h"
#interface ToDoViewController ()
#property NSMutableArray *toDoItems;
#end
#implementation ToDoViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.toDoItems = [[NSMutableArray alloc] init];
[self loadInitialData];
}
- (void)loadInitialData {
NSString *item1 = #"Testing";
[self.toDoItems addObject:item1];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ToDoCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ToDoCell" forIndexPath:indexPath];
cell.taskLabel.text = #"Testing";
NSLog(#"Fudge Monkeys: %#", cell.taskLabel.text);
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
#end
Issue is you are creating new objects for labels but then you are not adding them to superview but calling [self addSubview:label] ssigning them to properties does not add them to super view.
But this is totally unnecessary you should not init them in awakeNib. , and one thing more I will say about your code now you don't need to use #synthesis anymore as well. Any particular reason you are making IBOutlets strong?
Update you need to register nib with tableView first add following lines in viewDidLoad
[tableView registerNib:[UINib nibWithNibName:#"ToDoCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:#"ToDoCell"];
You can read more about this here
If the UILabel instances are connected in Interface Builder via IBOutlet, you must not initialize them explicitly.
Just delete the complete awakeFromNib() method
Add this in viewDidLoad
NSString *strName = NSStringFromClass([ToDoCell class]);
[self.toDoItems registerNib:[UINib nibWithNibName:strName bundle:nil] forCellReuseIdentifier:strName];
You are using xib file but not loading it by registering it for the tableView. This is required when you use separate xib file. If you do not register, then only class will load and xib won't. And your awakeFromNib won't be called.
First of all delete awakeFromNib and make sure that you gave Cell Identifier in cell nib file.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"ToDoCell";
ToDoCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (!cell)
cell = [[ToDoCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: cellIdentifier];
cell.taskLabel.text = #"Testing";
return cell;
}
Don't forget to add following code for register nib.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.tableView registerNib:[UINib nibWithNibName:#"ToDoCellTableViewCell" bundle:nil] forCellReuseIdentifier:#"ToDoCell"];
}
Here is the sample code for your problem.
May this help to solve your problem.
You .xib why cell should be set through:
- (UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
ToDoCell *cell = [tableView dequeueReusableCellWithIdentifier:[ToDoCell reuseIdentifier]];
if (!cell) {
cell = (ToDoCell *)
[[NSBundle mainBundle] loadNibNamed:#"ToDoCell" owner:self options:nil].firstObject;
}
cell.taskLabel.text = #"Testing";
NSLog(#"Fudge Monkeys: %#", cell.taskLabel.text);
return cell;
}
Case is following: I want to create UITableView from separate class.
Currently I have following:
// Menu.h
#interface Menu : UITableViewController <UITableViewDelegate, UIAlertViewDelegate> {
UITableView *tableView;
}
#property (nonatomic,retain) NSMutableArray *navigationItems;
- (void)initMenu:(UIView *)view;
#end;
Then
// Menu.m
#import <Foundation/Foundation.h>
#import "Menu.h"
#implementation Menu
- (void) initMenu:(UIView *)view {
self.navigationItems = [[NSMutableArray alloc] initWithObjects:#"One",#"Two",#"Three",#"Four",#"Five",#"Six",#"Seven",#"Eight",#"Nine",#"Ten",nil];
UIView *mainmenu=[[UIView alloc]initWithFrame:CGRectMake(0, 50, 320, 420)];
[mainmenu setBackgroundColor:[UIColor yellowColor]];
[view addSubview:mainmenu];
UITableView *menutableView = [[UITableView alloc] initWithFrame:view.bounds style:UITableViewStylePlain];
menutableView.backgroundColor = [UIColor whiteColor];
menutableView.delegate = self;
menutableView.dataSource = self;
[mainmenu addSubview:menutableView];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableViewi cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableViewi dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [self.navigationItems objectAtIndex:indexPath.row];
return cell;
}
#end
And in different .m file I call method:
...
#import "Menu.h"
...
- (void)viewDidLoad
{
[super viewDidLoad];
...
Menu *menu = [[Menu alloc] init];
[menu initMenu: self.view];
}
Running this will crash application and Xcode won't give any detailed report. However, if I combine Menu.m to the .m file where I'm calling "initMenu" it won't crash.
Also if I comment out menutableView.dataSource = self; it will run with our crash (no rows in table of course...).
Passing a view into an init method and then creating/adding subviews in said init method is an odd design pattern. Change your Menu class to a viewcontroller, then move your UIView and UITableView declarations to the viewDidLoad method. The crash is likely happening because the tableview is trying to display data before its parent view has finished loading.
I'm trying to subclass a UITableView. However, I'm unable to get an indexPath that isn't nil. The tableView has custom cells.
Here is my TouchTableView.h
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h> //I brought this in because I'm saving audio files in my app
#import "SimpleTableCell.h"
#protocol myTableViewDelegate;
#interface TouchTableView : UITableView <UITableViewDelegate, UITableViewDataSource, AVAudioRecorderDelegate, AVAudioPlayerDelegate, UITextViewDelegate, UITextFieldDelegate, UIAlertViewDelegate>
#property (nonatomic, weak) id<myTableViewDelegate> myDelegate;
#property (strong, nonatomic) NSMutableArray *sortedFiles;
#property (strong, nonatomic) NSString *simpleTableIdentifier;
#property (strong, nonatomic) SimpleTableCell *cell;
#property BOOL inverted;
-(void)refreshTable;
#end
#protocol myTableViewDelegate <NSObject>
- (void)selectedFile:(TouchTableView *)tableView withURL: (NSURL *) tableViewURL IndexPath:(NSIndexPath *)indexPath;
-(void)didDelete:(TouchTableView *)tableView IndexPath:(NSIndexPath *)indexPath;
-(void)setSortedFile:(TouchTableView *)tableView;
#end
I attach a longpress like so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//bring in your custom cell here
simpleTableIdentifier = #"SimpleTableCell";
cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"SimpleTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
[cell.textField setEnabled:NO];
//put in long press
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
[longPressGesture setMinimumPressDuration:1.0];
[cell addGestureRecognizer:longPressGesture];
}
}
return cell;
}
Then I have the following method for when the longpress activates:
- (void)longPress:(UILongPressGestureRecognizer *)gesture
{
if (![cell.textField isEnabled]) {
// only when gesture was recognized, not when ended
if (gesture.state == UIGestureRecognizerStateBegan)
{
// get affected cell
cell = (SimpleTableCell *)[gesture view];
// get indexPath of cell
NSIndexPath *indexPath = [self indexPathForCell:cell];
[self selectRowAtIndexPath:indexPath animated:NO scrollPosition:0];
[cell.textField setEnabled:YES];
[cell.textField becomeFirstResponder];
}
}
}
In my viewController in viewDidLoad I have:
self.touchTableView = [[TouchTableView alloc] init];
[self.tableView setDelegate:self.touchTableView];
[self.tableView setDataSource:self.touchTableView];
self.touchTableView.myDelegate = self;
The problem is that the indexPath is always nil. You'll note that I'm calling self instead of self.tableView because self is the tableView. Is there a way to get the indexPath?
Sorry for the mess all. rmaddy was right. Ridiculous. The solution:
in my ViewController.h I set up:
#property (weak, nonatomic) IBOutlet TouchTableView *tableView;
Then I changed the last code in my example to
[self.tableView setDelegate:self.tableView];
[self.tableView setDataSource:self.tableView];
self.tableView.myDelegate = self;
[self.tableView refreshTable];
Now I'm getting the index path.
I have a view controller loaded from xib with a table view and a text field as titleView in the navigation bar. Simple.
#interface ViewController () <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, weak) IBOutlet UITableView *tableView;
#end
#implementation ViewController
#synthesize tableView;
- (void)viewDidLoad {
[super viewDidLoad];
tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
[tableView reloadData];
UITextField *tf = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
self.navigationItem.titleView = tf;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 42;
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#""];
cell.textLabel.text = #"test cell";
return cell;
}
#end
I tap the text field, let the keyboard show and start to drag the table view to interact with keyboard.
The problem is when I release touch while interacting and let the keyboard fully show: it goes up without animation. If I release to hide it works.
Furthermore this is only in iOS 8. It works in iOS 7.
Am I missing anything?