If I have a view controller which implements two protocols:
#interface CustomerOperationsViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>
Is there any easy way to create the required callbacks for the protocols? Maybe an Xcode shortcut for implement methods? I'm going to the documentation each time for this.
Related, is it possible to put the delegate into a different file than the file owner? I don't see how to drag from the UI element to a class other than the file owner.
Just Command-Click on the delegate method and it will show you the delegate's header, so you don't need to browse the internet (e.g. Apple's Developer Site) for the appropriate docs. Also, in my implementation files I usually write code like this whenever I implement a delegate:
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// implementation here ...
}
#pragma mark - UITableVieWDataSource
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// implementation here ...
}
By Command-Clicking on the pragma mark text (UITableViewDelegate / UITableViewDataSource), the delegate header will be shown as well, less navigation to the header file of the current class to Command-Click the protocol. Just copy and paste the methods you need from the delegate's header.
Finally what's also useful is just start typing in Xcode (any implementation file that conforms to the protocol you want to autocomplete) ...
- tableView
and press escape. The tableView delegate methods will pop-up and you can select the one you want with tab, it will autocomplete. Same is true for delegates of other objects.
Welcome to XCode, one of the more frustrating IDEs out there. There isn't a particularly straightforward way to pre-populate callbacks, although they should come through in CodeSense. You could copy/paste from the header files, but you'll still need to manually edit some stuff.
As to your second question: yes - your delegate doesn't have to be the file owner, but normally if you were setting it to something else you'd do it programatically rather than via IB. Where/what did you want your delegate to be? Another view controller, or something different?
Related
I'm using Xcode Version 5.0 (5A1413) and targeting iOS7 for iPhone. I have nothing but a UIViewController with a Table View on it. Everything I've found says to just set the table's dataSource and delegate to the ViewController. No matter what I do the app just crashes immediately. The view never loads even though there's no code written manually at all. Is it no longer possible to put a table onto a non TableViewController?
You should implement all required methods from UITableViewDataSource for preventing crash:
#required
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
I think you did a common mistake from beginners. You deleted the main view in Interface Builder and then added a Table View.
You have to know that the UIViewController class has a UIView property called view. By default this property is set to the view created with the nib (or Storyboard) file.
To say that the Table View is the main viewcontroller's view you have to right click from the file owner to the tableview and select "view".
Another method if your not comfortable yet with this method is to set the tableview in the first view.
Here is the answer - How to add static TableView to ViewController
Really you need to implement required methods.
There is no such restrictions with iOS 7.0. You can still have a UIViewController in-act as a data source and delegate for UITableView. You just need to implement all the mandatory methods set by these protocols. Could you please update your question with your code and the exception causing the crash. Without this information it would be hard to provide a solution.
I am especially interested in seeing your cellForRowAtIndexPath the way you are constructing cells and reusing them.
Add the Delegate and Datasource to the interface:
#interface MyViewController : UIViewController
<UITableViewDataSource, UITableViewDelegate>
I have been practicing with table views and I know how to create them but I would like to have a better understanding about delegate and source when creating table views.
Can someone explain the need for a delegate and a source when creating table views?
Why do you need them?
What is happening when you connect delegate and source to File’s Owner or ViewController and why they need to be connected?
I guess I need a general explanation about delegates and source and what happens when you connect them to File’s Owner or ViewController?
The delegate and data sources allow the tableview to conform to the MVC design pattern, which is a recurring design pattern in Cocoa and Cocoa Touch.
The TableView itself provides the [V]iew part and the delegate provides the [C]ontroller part while the data source provides the [M]odel part.
When you connect the delegate and datasource in the NIB file you are creating this connection visually; you can just as easily do it programmatically.
Delegate:-
A delegate is an object that acts on behalf of, or in coordination with, another object when that object encounters an event in a program. The delegating object is often a responder object—that is, an object inheriting from NSResponder in AppKit or UIResponder in UIKit—that is responding to a user event. The delegate is an object that is delegated control of the user interface for that event, or is at least asked to interpret the event in an application-specific manner.
Data Source:-
A data source is like a delegate except that, instead of being delegated control of the user interface, it is delegated control of data. A data source is an outlet held by NSView and UIView objects such as table views and outline views that require a source from which to populate their rows of visible data. The data source for a view is usually the same object that acts as its delegate, but it can be any object. As with the delegate, the data source must implement one or more methods of an informal protocol to supply the view with the data it needs and, in more advanced implementations, to handle data that users directly edit in such views.
For Detail info goto
http://developer.apple.com/library/ios/#documentation/general/conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html
You dont need to make any connections if you are happy to write the following code:
tableview.delegate=self;
tableview.dataSource=self;
The UITableViewDataSource protocol is adopted by an object that mediates the application’s data model for a UITableView object. The data source provides the table-view object with the information it needs to construct and modify a table view.
Example:
Whereas a data source type object gives data to another object. For example again, the UITableViewDataSource protocol has methods such as cellForRowAtIndexPath and numberOfRowsInSection dictating what should be displayed in the table
The UITableViewDelegate of a UITableView object must adopt the UITableViewDelegate protocol. Optional methods of the protocol allow the delegate to manage selections, configure section headings and footers, help to delete and reorder cells, and perform other actions.
Example :
A delegate type object responds to actions that another object takes. For example, the UITableViewDelegate protocol has methods such as didSelectRowAtIndexPath for performing actions upon a user selecting a particular row in a table.
If your programming language doesn't support multiple inheritance, you must use delegate method. When you implement delegate method, you can use object functions such as super class. Example :
// define tableview row count
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
// define tableview height
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
}
// define specific tableview cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = MyCell();
return cell;
}
I want to add a tableview-look-a-like-login to my app, but it seems to be not that easy to implement. I tried to accomplish my goal using more then one approach, but i am not sure about which solution is the best.
For example, Dropbox and Facebook have a login page like this.
Here are my 3 approaches :
I added 2 UITextfields to my View (no border) and placed a . png behind, which looks like a tableviewcell. ( Not the best approach cause i want to use real tableviews )
I added a Container View to my ViewController placed a tableview with static Table Views inside. The Problem here is, that i dont know how to access the information inside my viewcontroller?
I added a tableview to my ViewController and used dynamic cells with it. Connected the outlets for delegate and datasource to my viewcontroller and initialized them with the delegate and datasource methods. The Problem here is, that i can not use static table views inside a uiviewcontroller.
Is there any better way of solving this problem ?
I would really like to know how to do this in a more elegant way.
EDIT:
A ContainerViewController basically solved this issue for me some month ago.
After embedding one into the main controller you can access it through the prepareForSegue function and define a protocol-based interface for that specific controller to interact with the embedded controller.
If you want to use static cells inside a regular UIViewController, just add the static cells and design them the way you like in interface builder, then connect the table cells as strong IB outlets (weak won't work, make sure they are strongly referenced). This will work flawlessly if you have a few table cells. Then set the view controller as the data source of the tablet view, implement -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section to return the number of cells and implement -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath to return your strongly referenced cell instance for the specified index path. I've used this method for a simple table view in my view controller that had four cells and it is working perfectly. For a large-dynamic data set, I definitely do not recommend this approach but for small, static tables, this does the job right.
I have an idea how to solve this. I think it's a clean way to do so. You do not need storyboard for this controller.
Make your controller subclass UITableViewController like so:
#interface YourViewController : UITableViewController
Then in your viewDidLoad you create the instances of the cells:
- (void) viewDidLoad {
usernameCell = [YourTextFieldCell new];
passwordCell = [YourTextFieldCell new];
}
The YourTextFieldCell is of course your own subclass of a UITableViewCell, which could be something like this:
#implementation YourTextFieldCell {
UITextField textField;
}
- (id) init {
self = [super init];
if (self) {
// Adjust the text's frame field to your liking
textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 200, 20)];
[self addSubview:textField];
}
}
// A getter method to access the textfield from the outside
- (UITextField *) textField {
return textField;
}
#end
Back in YourViewController:
- (NSInteger) tableView:(UITableView *) tv numberOfRowsInSection:(NSInteger) section {
return 2;
}
- (UITableViewCell *) tableView:(UITableView *) tv cellForRowAtIndexPath:(NSIndexPath *) indexPath {
if (indexPath.row == 0) {
return usernameCell;
} else if (indexPath.row == 1) {
return passwordCell;
}
return nil;
}
Do you get where I am going with this? This is how I think you should do it! Good luck!
I think your approach 2 is the best. If you need to access information in the table view controller, from your UIViewController (which will be the parent view controller), you can get a reference to that table view controller with self.childViewControllers.lastObject. In the viewDidLoad method of the UIViewController subclass, you could set yourself as the delegate of the table view with this line if you want:
[[(UITableViewController *)self.childViewControllers.lastObject tableView] setDelegate:self];
That way, you could implement the tableView:didSelectRowAtIndexPath: method in the view controller, which will get the information I'm guessing you need.
If you go with your option 2) using a storyboard and have a ContainerView containing your own subclass of UITableViewController with static cells then you can implement the prepareForSegue: method in your parent ViewController to take a reference to the UITableViewController (it'll be the destinationController of the segue) and also to pass itself down to the UITableViewController subclass if necessary (which should hold onto it with a weak reference).
Disclaimer - This answer will work for any size of UITableView, but if you're just making a login view, Tom's answer will work quite well.
I'm not sure if this will help, but what I did for this was create my own UITableView-esque subclass with a UITableViewCell-esque subclass as well.
This may not be what you want to hear, but I find what I made to be really helpful, since I've used it a number of times now. Basically, you have a UIView with the stylistic approach for the different types (10.0f - 20.0f cornerRadius and a 1px border (divide by UIScreen's scale property for retina). As for the cell, you'll want to have a full sized UIButton on it that responds to your table view for the touch events either with a delegate or by setting the target and tag inside your table view's class.
Last, you'll have a delegate system just like the UITableView for your information for building the specific tables.
In short, you'll need:
2 UIView subclasses (TableView and TableViewCell)
2 Delegates/Protocols (TableViewDataSource and TableViewDelegate)
Optionally
1 Delegate (TableViewCellResponseDelegate)
1 NSObject Subclass (Contains all of the information needed in each cell - Ease of use)
I found Can's solution to be the best / easiest, but unfortunately it breaks in XCode 5.1 --
I found a workaround which builds off the same basic idea, but unfortunately requires a little more involvement: http://www.codebestowed.com/ios-static-tableview-in-uiviewcontroller/
To summarize, you can add TableViewCells directly to views (and create IBOutlets from them, etc), but in order for them to get "moved" to the TableView properly, you need to remove them from the view in code, and you also need to set Auto-Layout constraints in IB.
for some reason I can't get my tableview to enter editing mode. It's a little harder than it might seem because I'm using some open source code to make a calendar (iCal esque) with a tableview under it, and it's not quite as straightforward as just using a regular tableview.
Basically, I have two classes. One (let's say Class A) is a UIViewController and the other (Class B) is that viewController's datasource and tableview delegate. In Class B, I've implemented
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
which works fine, but if I put an edit nav bar item in Class A (the view controller), nothing happens. Am I forgetting certain protocols, or certain special methods that I need to write? Thanks for any help, and apologies if I'm missing something, I'm just learning to work with other people's code.
EDIT:
I just implemented this code in Class A (the view controller)
- (void)setEditing:(BOOL)editing animated:(BOOL)animate {
self.tableView.editing = editing;
}
And that makes the editing circle come up, but it is not animated.
You almost have it already. Call super too.
- (void)setEditing:(BOOL)editing animated:(BOOL)animate {
[super setEditing:editing animated:animate];
[self.tableView setEditing:editing animated:animate];
}
In my application I have a UITextField inside a UITableViewCell. If I click inside the text field and add some text I find that if try to move the insertion point it works the first time but fails on subsequent attempts. I am completely unable to move the selection; no "magnifying glass" appears.
Even more curious, this "setting" seems to be permanent until I restart the application. And it affects all UITextFields on that screen and not just the one that I originally tried to edit.
If you want to see it yourself, try the "UICatalog" sample that comes with the iPhone SDK. Click "text fields" and then "edit" and play around with the text boxes.
I've done a lot of digging on this but it's pretty hard to Google for! The best references I've found are on Apple's support board and MacRumors formum (both reference a solution that apparently used to work on iPhone 2.0 but does work not with contemporary versions -- I did try).
My feeling that is that this is a bug in the OS, but I thought I'd throw this out to the SO crowd for a second opinion and to see if there are any workarounds. Any ideas?
Following benzado's suggestion, I tried building my application using the 2.0, 2.1 and 2.2 SDKs. I got the same behaviour in all versions. (Actually, something related but not the same broke in 2.2 but that's probably another question!)
I spent a lot of time on this but I finally think that I have it nailed.
The trick is that the table needs to be editable (i.e., its editing property needs to be set to YES). The good news is that you are now able to move the insertion point. Sometimes the magnifying glass doesn't appear or follow but your gesture always seems to work.
Does this still qualify as a bug? Perhaps. At the very least Apple's SDK documentation should be updated. I've raised a bug report with Apple to cover this (rdar://6462725).
Thanks to this post, I've been able to successfully get this to work properly in my app.
Something to add, however:
If you set your table to be editable, you'll likely get different behavior than you expect (indenting, editing widgets, no disclosure indicators, etc.). This surprised me but here's how to best deal with it:
In your UITableView delegate, implement:
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
Then, in your UITableViewCell's implementation, set your UITableView to be editable ONLY when you're actually editing:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
((UITableView *)[self superview]).editing = YES;
...
}
and disable editing when editing is done:
- (void)textFieldDidEndEditing:(UITextField *)textField
{
...
((UITableView *)[self superview]).editing = YES;
}
This will ensure that your table isn't in editing mode when you're not editing the cell, keeping things working smoothly.
Thanks!
Brian M. Criscuolo
Mark/Space Inc.
The answer by Brian M. Criscuolo is the closest, however its still not quite right - in my usage of SDK2.2.1 I find that I have to do the following:
To your UITableViewDelegate (which is often your UITableViewController) add both of the following:
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
and:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleNone;
}
and:
- (void)viewDidLoad {
[super viewDidLoad];
// do any other customisation here
self.uiTableView.editing = true;
}
If you don't put the top two delegate methods, the above will cause the delete icons next to each row, and the indentation of each row.
You shouldn't need to do anything with textfield delegates as Brian indicated (unless you have multiple rows and you want to respond to a didSelectRowAtIndexPath: event - which you don't seem to get while in edit mode - then you will need to also do as he suggests).
By the way - this seems fixed in SDK3.0 (although subject to change I guess)
It does sound like an OS bug. I would try to reproduce it by running the UICatalog sample against the 2.0, 2.1, and 2.2 SDKs to see if anything changes. (There's a bug related to table cell text alignment that occurs if you build for 2.2 but not if you build for 2.1, regardless of what version of the OS is on the device.)
If it turns out to make a difference, https://bugreport.apple.com/
I tried this with my application and it seems to work as expected. I get a magnifying glass every time. I am using the 2.1 SDK.
actually what works best seems to be to set the table "editing" property to true in "viewDidLoad" and adding these to the table delegate
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
you don't need to do anything in the text field delegate
In iOS 12 or later you can select table view cell in the storyboard, then enable User Interaction Enabled in the attribute inspector.