Cocoa-Touch: UIPickerView viewForRow crashing - iphone

I have a UIPickerView, in it's delegate I'm trying to customize the view for a row. I'm using the 3.1 SDK.
So in the delegate I have:
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
// view.backgroundColor = [UIColor redColor];
return view;
}
From the apple docs:
If the previously used view (the view parameter) is adequate, return that. If you return a different view, the previously used view is released. The picker view centers the returned view in the rectangle for row.
When I run this, my UIPickerView control doesn't have any items in it, and after a short while crashes.
When I remove this particular method (which is optional for the delegate), I can see the labels I set via the titleForRow method, and it will no longer crash.
I'm pretty new to cocoa (and cocoa-touch), I'm not sure the view.backgroundColor thing will work, but even when returning the unmodified old view (which I must do anyway for most rows) crashes my app.
Am I doing something wrong?

Yes, you implement either –pickerView:titleForRow:forComponent: or –pickerView:viewForRow:forComponent:reusingView:, but not both. What is happening is that it is not calling your –pickerView:titleForRow:forComponent: because it is using your –pickerView:viewForRow:forComponent:reusingView:. You are returning the reusingView: parameter, but that is nil the first time, because there was no "previously used view" for that row.

Related

Best approach to add Static-TableView-Cells to a UIViewcontroller?

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.

How to get the event when a picker view stopped scrolling

I need to perform some code when the user stop scrolling the picker, in other way, when the picker stop scrolling. The logic i want to follow is, once the picker stop scrolling, i get the current value and i do some database queries basing on that value.
In the picker view documentation, i don't see a delegate method that can help on such task. Any thoughts? thanx in advance.
whenever you scroll the picker view, didSelect delegate method call at the end of scroll
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
NSLog(#"Selected %i. ", row);
/// do it here your queries
}
try with above example and check your console
The delegate class has a method pickerView:didSelectRow:inComponent:, that you can use to detect the selected row.

iOS - setting outlets on a ViewController being used as a UITableView header

I'm using custom headers for my tableview...
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
CustomHeaderController *header = [[CustomHeaderController alloc]
initWithNibName:#"TableHeader" bundle:nil];
header.title.text = #"Test";
return header.view;
}
The title label is never set though. I even tried creating a viewWillAppear method and setting it there, but that didn't work. My outlets are set up too!
Thanks!
SOLUTION: View wasn't load on the return header.view call. Call header.view or add a viewDidLoad method to the class of header to get it to work! Thanks all!
So I'm not sure "title" here means what you think it means. Setting the title on a view controller isn't going to affect what's in the view. If you're trying to set a title on a section, you may just want to use tableView:titleForHeaderInSection. tableView:viewForHeaderInSection is for more complex section headers (like if you wanted to put buttons, or multiple rows of text or something like that). If it matters, you can use both of these methods. I'm not 100% certain on the order they're called, but I'm pretty sure it looks for a viewForHeaderInSection and then if that's nil it goes to titleForHeaderInSection.
The function expects a UIView to be returned, but you appear to be returning a UIViewController. Did you mean to do return header.view?

Iphone UIView implementation

How does one code this scenerio in iphone sdk?
In an expense app, when you want to add an expense, this view comes up.
After selecting "Clothing," another view slides up with a UIPickerView control that has a "done" button which will dismiss the UIPickerView. Below is a screen shot after hitting "Clothing."
I'm trying to figure out how one would slide up the UIPickerView half way up the screen with a "done" button on top of the "New Expense" view?
thank you in advance!
Use CoreAnimation and make the UIView with move from bottom to top.. and change the hidden property to true from false when required and vice versa..
Multiple UIViews can be nested as required take advantage of this to achieve what u need
You implement the UIPickerDelegate, then implement the methods that belongs to the UIPickerView.
So your interface file must contain this:
#interface YourViewController : UIViewController <UIPickerViewDelegate, UIPickerViewDataSource> {
Your viewController them implements these, or more, methods:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component;
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component;
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component;
t´You would then instantiate the picker, set its delegate and all other properties you need it to conform to.
You could then hook up a "listener" for keeping track on when it changed.
[datePicker addTarget:self action:#selector(didChangeDate) forControlEvents:UIControlEventValueChanged];
A good place to start is the UICatalog example from the Apple developer site.
This has a lot of Picker code and a bunch of other stuff that could help getting in the mindset Apple uses for building stuff with UIElements.
Hope it helps:) it is a large subject.
This post helped me; might help others: http://sdhillon.com/animated-uipickerview

How to detect edit mode on iPhone UITableView

For my iPhone app, I have an editable (for delete) table view. I'd like to be able to detect that the user has clicked the "Edit" button. See this image: http://grab.by/It0
From the docs, it looked like if I implemented :
- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
then I could detect it (although from the name of the method, I wouldn't think that). This proved not to work.
Any ideas on detecting this? The reason I want to is I want to hook up a "Delete all" button in the upper left hand corner when in delete mode.
thanks
It is probably not working as you expect because willBeginEditingRowAtIndexPath: is called before the editing starts.
If you want to check while in another method you need the editing property:
#property(nonatomic, getter=isEditing) BOOL editing
If you want to do something when the 'Edit' button is pressed you need to implement the setEditing method:
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
Which you'll find in UIViewController. (Well, that's the most likely place; there are others.)
Swift
Use below code accordingly:
open var isEditing: Bool // default is NO. setting is not animated.
open func setEditing(_ editing: Bool, animated: Bool)
When subclassing a tableviewcontroller (what most people are going to be doing most of the time since you have to override it's delegate methods just to put data into it...) you can just override the setEditing:animated: method to grab editing state changes.
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
NSLog(#"Editing %i", editing);
[super setEditing:editing animated:animated];
}
That passes the state change along to the super class, but lets you jump in the middle and detect the change, or alter it if you wanted...
The setEditing:animated: examples didn't work for me (on iOS 6.1) to detect the state changes that occur when you enter and exit delete confirmation mode. It seems that setEditing:animated: is only called once, when the table view goes into edit mode, but not on state changes of the cells. After some debugger fun, I arrived at a method to detect the cell state change.
My use case is different from yours. I just wanted to hide the label when the delete button is showing so that the other cell content doesn't overlap it when the Delete button slides in. (I'm using UITableViewCellStyleValue2, the one with the blue label on the left and black label on the right.)
(In your UITableViewCell subclass)
- (void)willTransitionToState:(UITableViewCellStateMask)state {
[super willTransitionToState:state];
if (state & UITableViewCellStateShowingDeleteConfirmationMask) {
// showing delete button
[self.textLabel setAlpha:0.0f]; // <-- I just wanted to hide the label
}
}
- (void)didTransitionToState:(UITableViewCellStateMask)state {
if (!(state & UITableViewCellStateShowingDeleteConfirmationMask)) {
// not showing delete button
[self.textLabel setAlpha:1.0f]; // <-- show the label
}
}
Kendall 's answer works. I did it in following way.
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
NSLog(#"Can edit %d", tableView.editing);
if (tableView.editing == 1) {
[self.editButtonItem setTitle:EDIT_BUTTON_TITLE];
}else {
[self.editButtonItem setTitle:DONE_BUTTON_TITLE];
}
return YES;
}
That method tells you when a user is editing a Cell, not put the table into editing mode. There is a method called when editing mode is entered, to ask each cell if it can be edited:
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
I don't think overriding setEditing:animated: makes sense, since you would have to subclass UITableView which is extra work and a class you need for no other reason, not to mention it would have to communicate the fact that editing had been turned on back to the controller.
One other option is to simply add the Edit button yourself - it's a built in UIBarButtonSystemItem, you can add it and then have it call your own method in which you do something specific then call setEditing:animated: on the UITableView itself.
The idea behind editing is that when editing is enabled, each cell is told to go to edit mode, and as asked if there are any specific editing controls that should be applied. So in theory there's no need to detect entry into editing mode beyond changing the appearance of cells. What are you trying to do when editing mode is entered?