I have the problem that I have created a delegate protocol, but the necessary methods are not executed, although I have implemented the protocol in my header file. Here are the detailed explanation:
I created an instance of my ViewController (TimeLineViewController), which will be displayed. This ViewController contains a UITableView, which in turn receives the individual Cells / Rows from one instance of my TableViewCell. So the ViewController creates an instance of TableCellView.
The TableViewCell contains a UITextView, which contains web links. Now I want, that not safari opens the links, but my own built-in browser. Unfortunately TableViewCell can not open a new ViewController with a WebView, so I decided to create a delegate protocol.
The whole thing looks like this:
WebViewTableCellDelegate.h:
#protocol WebViewTableCellDelegate
-(void)loadWeb;
#end
Then I created a instance WebViewDelegate in the TableViewCell:
id <WebViewTableCellDelegate> _delegate;
In the .m of the TableViewCell:
#interface UITextView (Override)
#end
#class WebView, WebFrame;
#protocol WebPolicyDecisionListener;
#implementation UITextView (Override)
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id < WebPolicyDecisionListener >)listener
{ NSLog(#"request: %#", request);
[_delegate loadWeb];
}
#end
- (void)setDelegate:(id <WebViewTableCellDelegate>)delegate{
_delegate = delegate;}
And in my TimeLineViewController I implemented the protocol with <> and the loadWeb-metode:
- (void)loadWeb{
WebViewController *web = [[WebViewController alloc] initWithNibName:nil bundle:nil];
web.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController: web animated:YES];
[web release];
}
And when the instance of the TableViewCell will be created in the TimelineViewController:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
MyIdentifier = #"tableCell";
TableViewCell *cell = (TableViewCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if(cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"TableViewCell" owner:self options:nil];
cell = tableCell;}
[cell setDelegate:self];
//…
}
It is the first time I created a own delegate-protocol, so maybe there are stupid mistakes. Also I´m learnung Objective-C and programming generally only for 4 weeks.
Thanks for your help!
EDIT: I think i found the problem, but I dont know how to resolve it. I try to use [_delegate loadWeb]; in the subclass of the UITextView (because that is the only way i can react on the weblinks) and the subclass can´t use [_delegate loadWeb];. I tried this in a other methode and it worked.
Your first problem is that:
id <WebViewTableCellDelegate> *_delegate;
should be:
id <WebViewTableCellDelegate> _delegate;
The id type is already a pointer reference to an instance.
Related
I am doing an exercise about tableView and tableViewCell. And I am having some problems with custom view cells.
I have created my own custom tableViewCell with .xib file called as newCellView.xib. I have required .h and .m files and I choose super class as UITableViewCell.
In my view controller called as TableViewController I was creating a table with default cells like this.
- (UITableViewCell *)tableView: (UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cell.textLabel.text = [showNames objectAtIndex:[indexPath row]];
cell.imageView.image = [UIImage imageNamed:[iconArray objectAtIndex:[indexPath row]]];
return cell;
}
Once I have created my own custom view. I imported newViewCell.h into my viewController and I updated the code like this.
- (UITableViewCell *)tableView: (UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *newCellViewString = #"newCellView";
newCellView *cell = [tableView dequeueReusableCellWithIdentifier:newCellViewString];
//newCellView *cell = [[newCellView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:newCellView];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"newCellView" owner:self options:nil];
cell = (newCellView *)[nib objectAtIndex:0];
}
But when I run the app I see a blank page, like I have no related view. Maybe I forgot some connections to add. I can't even see empty cells to view. Only a white blank page. Any help would be great. Thanks.
EDIT FOR OTHER FILES
Here are my .h and .m files.
#import <UIKit/UIKit.h>
#interface newCellView : UITableViewCell <UITableViewDelegate>
#property (nonatomic, strong) IBOutlet UILabel *nameLabel;
#property (nonatomic, strong) IBOutlet UIImageView *iconImageView;
#property (retain, nonatomic) IBOutlet UIButton *extendButton;
#end
.m file
#import "newCellView.h"
#implementation newCellView
#synthesize nameLabel;
#synthesize extendButton;
#synthesize iconImageView;
- (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
}
- (void)dealloc {
[extendButton release];
[nameLabel release];
[iconImageView release];
[super dealloc];
}
#end
When dealing with custom cell, I'm always trying to encapsulate everything related to the cell in a UITableViewCell subclass (including the NIB / outlets / ..) and use the tableView registerClass: forCellReuseIdentifier: method to tell my table which class to use for its cells.
In your example to do so you could:
In your newCellView.m, add the nib loading in the cell init:
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"newCellView" owner:self options:nil];
self = [nib objectAtIndex:0];
}
return self;
}
Make sure all Outlets connections are correct. (i.e. your UITableViewCell in your Nib is of class NewCellView,..)
Then in the viewDidLoad of your controller you tell your table to use newCellView for its cells:
[yourTableView registerClass:[newCellView class] forCellReuseIdentifier:#"newCellView"];
Finally in the cellForRowAtIndexpath:
newCellView *cell = [tableView dequeueReusableCellWithIdentifier:#"newCellView"];
if (cell == nil)
{
cell = [[NewCellView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"newCellView]";
}
I think, the problem with your question is that the custom cells are not attached to the custom class cell.
When you create a UITableViewCell class, it does not create a xib along with it.
Then you need to create a xib file, that needs to be attached to the custom class file.
Create an EMPTY NIB file, with no content. Then add a uitablecustomcell through the objects,
and when you do add the new object, GO TO File Inspector and in File Name enter the name newCellView.
Now the custom cells will display EMPTY rows.
Now, add several views to the custom cell and attach those views via IBOutlets created in .h files, namely nameLabel, iconImageView, extendButton.
This is a simple error I have encountered before. You forgot to typecast. it should be:
cell = (newCellView*)[nib objectAtIndex:0];
If this does not resolve your issue make sure you have your xib file set to use the "newCellView" class and not the default "UITableViewCell" class.
Also, if you created a tableview manually and added it as a subview of another view or set it as your view in the loadView method or similar, rather than subclassing UITableViewController make sure you set the frame for the tableview and that you added it as a subview.
You may want to remove the from your newCellView.h class. The delegate for the tableview should not be a view or a subclass of one. Especially not the cell that the tableview will be presenting. The UITableViewController should be receiving the delegate methods.
I found the answer. Everything was working good in my code except the positioning of the cell view's.
Project was using autolayout as a default property. So whenever i see the white page actually cells were out of bounds. I disabled autolayout and set movement properties of the items from size inspector.
Thanks for efforts.
I know this is an oft-asked question/problem. I've looked through a bunch of Q&A for my problem, but I guess I'm a little thick, because I didn't see an answer anywhere.
I have a file with in an array that I would like to use to populate a tableView.
The problem is that it's not being called. Neither is numberOfRowsInSection or numberOfSectionsInTableView. I far as I can see, only viewDidLoad was called.
I have 1 section, the number of elements in my array equals 3 (as opposed to nil).
Relevant code is here...
- (void)viewDidLoad {
[super viewDidLoad];
FileControl *fileArray = [[FileControl alloc] init];
matArray = [fileArray findUniqueItemsInArray:0 :[fileArray setFileToArray]];
[fileArray release];
NSLog(#"%i \n %#", [matArray count], matArray); // matArray is filled.
NSLog(#"ViewDidLoad"); }
- (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] autorelease];
}
NSLog(#"CellForRowAtIndexPath");
NSString *text = [matArray objectAtIndex:[indexPath row]];
[[cell textLabel] setText:text];
return cell; }
#interface MaterialTableViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource> {
IBOutlet UITableView *materialTableView;
NSArray *matArray;
}
#property (nonatomic, retain) NSArray *matArray;
#end
The other methods are standard.
I guess my problem lies in that I don't completely understand the flow well enough.
Any help would be greatly appreciated. Thanks in advance.
Have you set your UIViewController subclass to be the delegate and dataSource of the UITableView in question? Without doing so, none of the methods you mention will be called.
I suppose you are using a UITableViewController.
If you are using UITableView it is a little bit more complicated (in this case you need to implement UITableViewDelegate, UITableViewDataSource protocols).
[update] This is not your case, you are using UITableViewController.
Add this line to the end of your viewDidLoad method:
[self.tableView reloadData];
Or move this:
FileControl *fileArray = [[FileControl alloc] init];
matArray = [fileArray findUniqueItemsInArray:0 :[fileArray setFileToArray]];
[fileArray release];
to the init method. Your init method should look like this:
- (id)initWithStyle:(UITableViewStyle)style {
if ((self = [super initWithStyle:style])) {
FileControl *fileArray = [[FileControl alloc] init];
matArray = [fileArray findUniqueItemsInArray:0 :[fileArray setFileToArray]];
[fileArray release];
NSLog(#"%i \n %#", [matArray count], matArray); // matArray is filled.
NSLog(#"ViewDidLoad");
}
return self;
}
If you do not see any message in the log, it means that you are not using that method to initialize your object.
Please show all your code in the .m and .h files.
I have a suggestion. Just give a shot for a test. In the - (void)viewDidLoad declare this.
instead of : matArray = [fileArray findUniqueItemsInArray:0 :[fileArray setFileToArray]];
Use this: matArray = [[NSArray alloc] initWithArray:[fileArray findUniqueItemsInArray:0 :[fileArray setFileToArray]]];
Did you use
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.matArray count];
}
make sure you have added your tableview delegate to your .h file as shown below.
#interface YourViewController : UIViewController <UITableViewDelegate> {
}
Also make sure that you have connected your datasource and delegate in interface builder.
Do this by doing:
double click on your .xib file so it opens in interface builder.
left click on your tableview just once so its highlighted blue
right click on your tableview now and a menu should pop up.
drag the datasource & delegate to the files owner box.
make sure to save changes while in interface builder.
This should "connect" all the pieces needed to get it to work.
Happy Coding!!
have seen similar questions but couldn't find a definitive answer.
Having mastered regular tables of most types, I am doing some conceptual experiments with custom table cells to get familiar with how this works. I want to have a custom subclass of UITableViewCell which loads a nib to the contentView. I may want to implement different editing styles at some later point but want to reuse the custom view in different parts of my application, however, i'm having problem receiving the didSelectRowAtIndexPath message in my calling UITableViewController.
Here's the hierarchy which is built from a basic view template.
CustomCellViewController: A stock XCode objective-c class sublcassed from UITableViewCell
#interface CustomCellViewController : UITableViewCell {
IBOutlet UILabel *lbl;
}
#property (nonatomic, retain) IBOutlet UILabel *lbl;
#end
#implementation CustomCellViewController
#synthesize lbl;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
NSArray *a = [[NSBundle mainBundle] loadNibNamed:#"customCellView" owner:self options:nil];
UITableViewCell *tc = [a objectAtIndex:0];
NSLog(#"Cell loaded from nib");
[self.contentView addSubview:tc];
}
return self;
}
.. the other stock methods are unchanged ..
#end
I realise that the init method could be simplified but this is my first attempt.
The XIB file's owner is my custom class (CustomCellViewController), has a UITableViewCell and a label (linked to the outlet 'lbl' on it) positioned half way accross, leaving plenty of the underlying UITableViewCell clickable.
RootViewController is a standard, stock XCode subclass of UITableViewController
RootViewController sets up an instance variable "CustomTableCellController *myCustomCell"
The cellForRowAtIndexPath: is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"myCustomCell";
CustomCellViewController *cell = (CustomCellViewController *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[CustomCellViewController alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
NSLog(#"Creating New cell for position %d", indexPath.row);
} else {
NSLog(#"Reusing cell for position %d", indexPath.row);
}
// Configure the cell.
cell.lbl.text = [NSString stringWithFormat: #"Hi There %d", indexPath.row];
return cell;
}
And in the same RootViewController.m, my didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Cell tapped at row %d", indexPath.row);
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
*/
}
Designed at present, to purely output a log message when tapped.
numberOfSectionsInTableView returns 1
numberOfRowsInSection returns 50
This all compiles and runs fine, iPhone simulator starts, I see a table on the display, my output log confirms it has created 9 versions of CustomCellViewController and I can see the reuse stack via the NSLog() comments.
I just can't select a row, doesn't matter where I click in the custom cell, nothing gets to my didSelectRowAtIndexPath: in my RootViewController which is where I expect it.
Is it that I have I not set a delegate somewhere and if so, how? Do I need to do this via a first responder? (ie, create a method in my CustomCellViewController class, link the UITableViewCell from the XIB to that method and then call [super didSelectRowAtIndexPath] - but how do I pass the indexPath?
Am I not responding to a message from my XIB in it's owner and then passing it on (is this how I do it?)
I read through all the apple docs to get to this stage but couldn't quite decipher how touch messaging happened.
Slightly confused!
May be you have forget to set data source and delegate if the tableview object set it as like below
tbl.delegate = self;
tbl.dataSource = self;
I am trying to refresh an UITableView every time I navigate the the view that contains this Table.
I Have a ViewController and a custom UITableViewController that manages to set the Table Correctly when the application starts, using an NSMutableArray contained inside the controller.
When I navigate to the page containing the table, the ViewController calls a function that gets the data from a server with an HTTP request and parse it in an NSMutableArray.
Now here is my problem. I manage to send this array to my UITableViewController, but when I want to refresh my tableView, nothing happens.
I tried to use [myTable reloadData], but it doesn't calls the numberOfRowsInSection, or cellForRowAtIndexPath functions. I saw that people with the same problem solved it using [self.myTable ReloadData], but I get an error :
accessing unknown getter/setter method
I am pretty new to objective-C, and this error is still a bit mysterious to me as I get it a bit randomly.
Anyway, there is a high probability that I made a mess with the declaration of the UITableViewController (where am I supposed to declare it?) and with the Interface Builder links, so this can be a clue to find the solution.
Any one have an idea?
Thank you very much!
EDIT : Here is my tableview controller class:
#import "MyCell.h"
#class Mycell;
#interface MyTableController : UITableViewController {
IBOutlet MyCell * myCell;
IBOutlet UITableView * myTable;
NSMutableArray *data;
}
#property (nonatomic, retain) IBOutlet UITableView * myTable;
- (void) EditTable : (NSMutableArray*) param;
#end
And now the .m:
#implementation MyTableController
#synthesize myTable;
- (void) viewDidLoad {
[super viewDidLoad];
myTable = [[UITableView alloc] init];
data = [[NSMutableArray alloc] init];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [data count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"MyCell";
MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; >
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"MyCell" owner:self options:nil];
for (id currentObject in topLevelObjects){
if ([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (MyCell *) currentObject;
}
}
}
NSString *datastring = [listenom objectAtIndex:indexPath.row];
[cell setCell: datastring ];
return cell;
}
- (void) EditTable : (NSMutableArray*) param{
//This function is called by the ViewController when the user goes to the page containing the view
data = param; //The param array contains the data from the HTTP request
[self.tableView reloadData];
[self.myTable reloadData]; //I tried both, but only the first one actually calls the previous functions
}
You have a number of problems in this code sample. I'll point out a few of them here but I highly recommend reading the relevant Apple documentation at:
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/Introduction/Introduction.html
and
http://developer.apple.com/library/ios/#documentation/userexperience/conceptual/TableView_iPhone/AboutTableViewsiPhone/AboutTableViewsiPhone.html
Some issues in your code:
Since the class MyTableController is a subclass of UITableViewController you don't need the attribute and property for myTableView. The tableView property is defined and initialized as part of UITableViewController's implementation with its dataSource and delegate set to the UITableViewController instance. This is why [self.tableView reloadData] is calling your delegate and dataSource protocol methods.
You are also using interface builder so if you did want to create your own subviews you should either do that within IB and set the outlet there or do it in your code which means creating the subview(s) in viewDidLoad and then adding them to your view with [view addSubview:mySubView].
A better way to set the data for your table would be to create a property for your data attribute and call setData from the view controller that has initialized the MyTableController instance. You would use the setData: method to do this. You can call [self.tableView reloadData] in setData. You don't need to explicitly reload the table when the view is loaded as this is done automatically. A more minor point, if you stay with EditTable I would rename it to be more descriptive and to use camel case (e.g. setDataForTable`) to be consistent with iOS conventions.
You don't show any init/alloc for the listenom attribute referenced in tableView:cellForRowAtIndexPath:. Did you mean to use data instead?
Is your MyTableController.m file the complete version? If so, you are missing viewDidUnload and dealloc methods. Both of which are required. viewDidUnload should release any objects allocated in viewDidLoad and dealloc should release anything retained by the controller (including objects released in viewDidUnload.
As you are using tableViewController you should be able to use self.tableView instead to reload the data like this
[self.tableView reloadData];
you need to synthesize first then you can use self.myTable
do on the top
#synthesize myTable
and then
[self.myTable reloadData];
I created a instance of my ViewController(TimeLineViewController), which will be presented. This ViewController contains a UITableView, which gets the cells from a instance of my TableViewCell. So the ViewController creates an instance of the TableCellView.
The TableViewCell contains a UITextView with enabled weblinks. Now I want to disable the function that safari opens. I did a subclass of the UITextView and:
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id < WebPolicyDecisionListener >)listener
{ NSLog(#"request: %#", request); //Console shows the link
}
Now I want that with a click on the weblink a new ViewController(WebViewController) appears. The problem is that the TableViewCell can´t "open" a new ViewController. So I tried this:
In the TableViewCell:
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id < WebPolicyDecisionListener >)listener
{ NSLog(#"request: %#", request);
TimeLineViewController * web = [[TimeLineViewController alloc]init];
[web loadView];
}
And in the ViewController:
- (void)loadWeb{
WebViewController *lust = [[WebViewController alloc] initWithNibName:nil bundle:nil];
lust.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:lust animated:YES];
[lust release];
}
The problem is that he always just reload the TimeLineViewController, but doesn't load the WebViewController. Why? How can I fix it?
(I know that the WebViewController doesn't get the weblink in my posted code and I know how to do it. That shouldn't be the problem, when I know, how to fix my problem.)
Thanks for your help! If you have questions, just ask - Sorry for my bad English.
UPDATE:
I did what Frank mention, but I doesn't work. I created a WebViewTableCellDelegate.h with:
#protocol WebViewTableCellDelegate
-(void)loadWeb;
#end
Then I created a instance variable of the WebViewDelegate in the TableViewCell:
__weak NSObject <WebViewTableCellDelegate> *_delegate;
and in the .m:
#interface UITextView (Override)
#end
#class WebView, WebFrame;
#protocol WebPolicyDecisionListener;
#implementation UITextView (Override)
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id < WebPolicyDecisionListener >)listener
{ NSLog(#"request: %#", request);
[_delegate loadWeb];
}
#end
In my TimeLineViewController I implemented the WebViewTableCellDelegate with <> and in the line, where I create the cells, I set the owner to self:
TableViewCell *cell = (TableViewCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if(cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"TableViewCell" owner:self options:nil];
cell = tableCell;
}
Why doens´t it work? There is no error-warning.
I would do the following:
Create a WebViewTableCellDelegate protocol (look up how to create a protocol on Apple's site). The protocol should include your loadWeb: method.
Have your TimeLineViewController implement the protocol and give your UITextView subclass an instance variable of type <WebViewTableCellDelegate>.
When you're creating the table view cell, set it's delegate to self (the TimeLineViewController).
When someone taps a link, call [delegate loadWeb:request]
Have your loadWeb: method accept an NSURLRequest and load it into the web view.
That should do the trick.
I don't think it is a good idea to put a UIWebView in every cell. Consider an implementation where you simply utilize the tableView:didSelectRowAtIndexPath: method in the UITableViewDelegate protocol to present your UIWebView when a particular cell is tapped.