How can I add a row in UIPickerview - ios6

I have a UIPickerView, the green button to add a new record in UIPickerView. I can add a new record, but it does not appear cause I can not change the number of rows in the component.
How can i add a record and a row in UIPickerView?
Thanks!
My UIPickerview:
https://docs.google.com/file/d/0B26lA9znN3CUSTdvX21kUF92MzA/edit?usp=sharing
EDITED: When I click on the green button, it displays a UIAlertView TextInput where I add a new value in my NSMutableArray, so the number of records changes. So when a ReloadAllComponents he not adds an extra row in UIPickerView because I already set an amount in numberOfRowsInComponent method.

Assume you manage an NSMutableArray for your pickerview records. In this case you have to manage 3 methods to achive this:
1. your green button handling method
-(IBAction) greenbuttonPressed:(id)sender{
// add new record to your NSMutableArray (mypickerviewrowsarray)
[self.myPickerView reloadAllComponents];
}
2. pickeview's datasource method, numberOfRowsInComponent
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return [self.mypickerviewrowsarray count];
}
3. pickerview's delegate method, titleForRow
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return [self.mypickerviewrowsarray objectAtIndex:row];
}
To do it right, make sure you set your pickerview's delegate, also you set your viewcontroller to implement the UIPickerViewDelegate and UIPickerViewDataSource.
Hope you can use this!

Related

How to maintain array content when clicking back navigation bar button to previous View Controller?

I have two View Controllers A & B.
In ViewController B, I have a button that adds an item to an array with a click
var list = [Items]()
#IBAction func addButton(_ sender: UIButton) {
let indexPath = IndexPath(row: sender.tag, section: tagPassedOver)
list.append(Items(category: "Toys", name: toys[indexPath.row], price: toysPrice[indexPath.row]))
}
ViewController A
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if self.isMovingFromParent {
// how do i send the data back in here??
}
}
Problem: When I click the back button in the navigation bar of View Controller B to go to View Controller A, my "list" array has been reset. So, my previous elements are no longer stored in the array, and if I click the button in View Controller B it creates a brand new array. I found something to help here but I do not know what insert in the if statement. Or is there a way to do this with segues? I just need to know how to send data backwards (Lmk if you need more clarification, I had to simplify my code to get an answer for this specific issue)
Goal: be able to go back/forth between views and preserve the contents of my array
One way to accomplish this would be by making your ViewController A be a delegate for ViewController B. If you aren't familiar with delegation, you will just need to define a protocol that declares the tasks the delegate (ViewContollerA) should handle and then add a variable to 'B' to store the delegate. 'A' will need to declare conformance to the delegate protocol and implement it. Then all 'B' needs to do is call it when needed.
In View Controller A:
class ViewControllerA: UIViewController, DataCollectorViewControllerDelegate {
func showViewControllerB() {
viewControllerB.delegate = self
viewControllerB.list = dataArray
//Do stuff to show ViewControllerB
}
//Will get called when ViewControllerB exits
func dataViewControllerWillExitWithData(_ dataArray: [MyDataType]){
//Do something with dataArray
}
}
In ViewControllerB:
protocol DataCollectorViewControllerDelegate: AnyObject {
func dataViewControllerWillExitWithData(_ dataArray: [MyDataType])
}
class ViewControllerB: UIViewController {
weak var delegate: DataCollectorViewControllerDelegate?
var list = [Items]()
func exit() {
self.delegate?.dataViewControllerWillExitWithData(list)
//Do what you need to exit
}
}
For more about the delegation pattern: delegation
Other things to think about:
Does B need to store data at all? Maybe B can just report to the delegate (A) what selection was made and let A own the data.

NSArrayController: Doesn't update bold attribute

I have a tableview bound to a NSArrayController. The ArrayController gets it´s data from an array [NSObject]:
class Fruits : NSObject {
var name:String
var price:Double
// If I set 'isBold = true' here manually,
//the font in my tableview becomes bold (from start of the application).
dynamic var isBold = false {
didSet {
print(isBold)
}
}
override init() {
name = "name"
price = 0.0
}
init(name:String, price:Double) {
self.name = name
self.price = price
}
}
So everything works fine as long as I set the ìsBold`value manually.
If I call in ViewController:
Fuits.init().isBold = true
tableView.reloadData()
The correct ìsBold`value is printed, but in the tableview nothing changes. Any ideas why the ArrayController doesn't notice the updated value?
EDIT:
I found a way how it is working. But maybe just a hint why it is not working like above. This solution works with iteration, I don´t think it is how it's supposed to be.
func changeBold(bold: Bool) {
if bold == false {
self.dataArray.forEach { fruit in
fruit.isBold = false
} else {
self.dataArray.forEach { fruit in
fruit.isBold = true
}
}
}
Edit: I misinterpreted what you had written. I thought the bindings settings panel was for the table view, not the table cell view. The rest of what you wrote makes more sense to me.
Your line Fruits.init().isBold = true is a bit odd syntax-wise, but the bigger point is that you're creating a new instance of Fruits and setting its bold attribute but not doing anything with the instance, so it goes away once it's out of scope. You need to change the property of the instance stored in the cell view's objectValue property (because that's the instance that's set by the bindings mechanism, which your cell view represents).
Also, be aware that "editing an object behind the controller's back" will also usually cause the symptom of changes not appearing in the UI. Example: if you remove an instance of Fruits from your array directly, you'd have to tell the array controller to refresh to see the changes (which can lose selection and scroll information in a table or some other UI state). Instead, you should access the array controller's arrangedObjects property and remove / add / insert there so the array controller is "aware" of the changes. You'll need to read a lot more on the bindings mechanism to understand when / why to make changes through a controller vs. directly, but it's an aspect of Cocoa Bindings of which you must be aware.

collectionView, didSelectItemAtIndexPath issue

I have an interesting issue with collectionView, specifically, didSelectItemAtIndexPath.
My app has one particular view with a background image, which is accessed via an SQL. Within that view is a popover, with a collectionView, to change the parent view's background, and access other "sets" available for purchase.
My issue is with the changing of the background. All items display correctly in the collection view and the parent's background will change as it should. The problem is that the item selected is not the one that displays, even though via NSLog, I can see that the correct image is being selected and passed back to the parent. There is a corresponding NSLog in the parent, which shows the same image id as the popover's NSLog.
The first item in the collectionView, when tapped, will change the background of the parent to white, as if there was no image available. Tapping the second cell will display the first cell's image. Tapping the third cell will display the second cell's image... and so on. There are 15 images available to select from.
This is the didSelect method:
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
if ([_currentView isEqualToString:#"New_Journal"])
{
MyAppDelegate *appDelegate = (MyAppDelegate *)[UIApplication.sharedApplication delegate];
// to get the variable back to the parent
appDelegate.loadBackgroundID = indexPath.row;
// interact with the SQL via _settingsDao
[_settingsDao updateJournalBackgroundId:_journalId withBackgroundId:appDelegate.loadBackgroundID];
NSLog(#"Selected background: %d", indexPath.row);
// notification to let EntryView know the background has changed
[NSNotificationCenter.defaultCenter postNotificationName:#"aBackgroundChanged" object:self];
}
}
Edit to show SQL interaction method:
-(void)updateJournalBackgroundId:(int)journalID withBackgroundId:(int)newBackgroundId
{
NSString *query = #"UPDATE BgIconBorderSettings SET background_id = ? WHERE journal_id = ?";
FMDatabase *db = [dbUtils sharedDB];
[db executeUpdate:query,[NSNumber numberWithInt:newBackgroundId],[NSNumber numberWithInt:journalID]];
if([db hadError])
{
NSLog(#"Error %d: %#",[db lastErrorCode],[db lastErrorMessage]);
}
}
Anyone have any ideas on how to get the correct image of the tapped cell to display? Thanks for any assistance in solving this issue.
As jhilgert pointed out... it was the SQL database. It started at 1 and not 0. Changed the ids and it works as expected.

How to properly add and delete objects to tableview with custom tableviewcells?

I'm currently developing an app that behaves like the Messages.app. The MasterViewController is the main view where it loads a table of the contact name, time and a snippet of the most recent message. When you tap a specific cell, it slides to the DetailViewController where it loads the messages I sent to the contact with the latest, complete message. Hitting the back button goes back to the MasterViewController. Tapping the rightBarButtonItem opens up a ComposeViewController (modal) where the user can compose a message to a specific contact. The difference of this app to the default Messages.app is that it has a delay timer before the message is sent. The ComposeViewController has a textfield to input the message, button to select a contact, button to pick a time delay, button to send, button to cancel the timer, and a button to dismiss the ModalViewController.
I removed the ability to send an actual SMS message entirely. I just presented the user with an alert view telling him/her that the message was sent and if he/she wants to compose a new one. Hitting Cancel will dismiss the ModalViewController and go back to the MasterViewController.
Problem is, I can't make the rows appear on the table and also have the ability to add and remove cells in the table.
Here's some code inside my MasterViewController's viewDidLoad:
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Delete button to delete messages
UIBarButtonItem *deleteBarButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemTrash
target:self
action:#selector(deleteText)];
self.navigationItem.leftBarButtonItem = deleteBarButtonItem;
// Compose button to go to compose messages
UIBarButtonItem *composeBarButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCompose
target:self
action:#selector(composeText)];
self.navigationItem.rightBarButtonItem = composeBarButtonItem;
[deleteBarButtonItem release];
[composeBarButtonItem release];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *message = [defaults objectForKey:kMessageText];
NSString *contactname = [defaults objectForKey:kContactNameText];
NSString *timestamp = [defaults objectForKey:kTimeStampText];
[messageDetails initWithObjectsAndKeys:contactname, kContactNameKey, message, kContactMsgKey, timestamp, kContactTimeKey, nil];
NSMutableArray *messageInfo = [[NSMutableArray alloc] initWithObjects:messageDetails, nil];
self.messagesList = messageInfo;
[messageInfo release];
[super viewDidLoad];
Here's the code in cellForRowAtIndexPath:
CustomCellViewController *customCell = (CustomCellViewController *)[tableView dequeueReusableCellWithIdentifier:#"CustomCellViewController"];
if (customCell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCellViewController"
owner:self
options:nil];
for (id oneObject in nib) if ([oneObject isKindOfClass:[CustomCellViewController class]])
customCell = (CustomCellViewController *)oneObject;
}
NSUInteger row = [indexPath row];
NSDictionary *messages = [self.messagesList objectAtIndex:row];
customCell.nameLabel.text = [messages objectForKey:kContactNameKey];
customCell.nameLabel.textColor = [UIColor whiteColor];
customCell.messageLabel.text = [messages objectForKey:kContactMsgKey];
customCell.messageLabel.textColor = [UIColor lightGrayColor];
customCell.timeLabel.text = [messages objectForKey:kContactTimeKey];
customCell.timeLabel.textColor = [UIColor blueColor];
customCell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return customCell;
Here's the code for deleting cells:
- (void)tableView:(UITableView *)tableView commitEditingStyle(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Delete the row from the data source.
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]withRowAnimation:UITableViewRowAnimationFade];
[messagesList removeObjectAtIndex:indexPath.row];
[self.tableView reloadData];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
Your final code snippet where you are creating a new instance of the master view controller is the problem.
This is not the view controller you are looking for. Since you've presented the detail view controller modally, you can access the master controller via the parentViewController property of the detail controller:
MasterViewController *master = (MasterViewController*)self.parentViewController;
Other design patterns that are typically used in this situation:
Create the new object in your master controller and insert the row in your table before passing the new object to the detail controller for updating
Create a delegate protocol for the detail controller, which your master controller conforms to. Set the detail controllers delegate to the master controller when pushing it
The latter is almost what you are doing for all practical purposes, except your detail controller knows more than it needs to about the master controller (i.e you are importing the whole master .h file rather than just knowing it conforms to a protocol).
Regarding your data structure, I don't understand how you expect to have multiple rows in here - you store one message in user defaults and then create an array with that message. I know you don't intend to use defaults to store this in the end, but I would expect to see an array of dictionaries stored in defaults under a single key, and then each dictionary represents a row in your table, with the various details stored as strings in the dictionary against your message, contact name keys etc.
You have to make a mutable version of the array returned from defaults as it always returns an immutable array.
In your cellForRow... method you'd then get the appropriate dictionary from the array and populate the cell from it.
When adding a new row you'd create a new dictionary to pass to your detail controller.
When deleting a row you'd remove the relevant dictionary from the array.

ExtJS: Added grid rows wont de-highlight

When adding a rows to a grid, and then clicking on it, it gets selected (and highlighted). Then, clicking elsewhere but the new row remains highlighted (so now there are to highlighted rows).
Please, does anyone know what the problem could be? How to make it behave normally, i.e. clicking a row deselects (de-highlights) the other one?
After I reload the page (so the new row is not new anymore), everything works as expected.
Edit: Here's the code for adding rows:
var rec = new store.recordType({
test: 'test'
});
store.add(rec);
Edit 2: The problem seems to be listful: true. If false, it works! But I need it to be true so I'm looking at this further... It looks like as if the IDs went somehow wrong... If the ID would change (I first create the record and then the server returns proper ID, that would also confuse the row selector, no?)
(Note, correct as ExtJS 3.3.1)
First of all, this is my quick and dirty hack. Coincidentally I have my CheckboxSelectionModel extended in my system:-
Kore.ux.grid.CheckboxSelectionModel = Ext.extend(Ext.grid.CheckboxSelectionModel, {
clearSelections : function(fast){
if(this.isLocked()){
return;
}
if(fast !== true){
var ds = this.grid.store,
s = this.selections;
s.each(function(r){
//Hack, ds.indexOfId(r.id) is not correct.
//Inherited problem from Store.reader.realize function
this.deselectRow(ds.indexOf(r));
//this.deselectRow(ds.indexOfId(r.id));
}, this);
s.clear();
}else{
this.selections.clear();
}
this.last = false;
}
});
And this is the place where the clearSelections fails. They try to deselect rows by using ds.indexOfId(r.id) and it will returns -1 because we do not have the index defined remapped.
And this is why we can't find the id:-
http://imageshack.us/photo/my-images/864/ssstore.gif/
Note that the first item in the image is not properly "remapped". This is because we have a problem in the "reMap" function in our Ext.data.Store, read as follow:-
// remap record ids in MixedCollection after records have been realized. #see Store#onCreateRecords, #see DataReader#realize
reMap : function(record) {
if (Ext.isArray(record)) {
for (var i = 0, len = record.length; i < len; i++) {
this.reMap(record[i]);
}
} else {
delete this.data.map[record._phid];
this.data.map[record.id] = record;
var index = this.data.keys.indexOf(record._phid);
this.data.keys.splice(index, 1, record.id);
delete record._phid;
}
}
Apparently, this method fails to get fired (or buggy). Traced further up, this method is called by Ext.data.Store.onCreateRecords
....
this.reader.realize(rs, data);
this.reMap(rs);
....
It does look fine on the first look, but when I trace rs and data, these data magically set to undefined after this.reader.realize function, and hence reMap could not map the phantom record back to the normal record.
I don't know what is wrong with this function, and I don't know how should I overwrite this function in my JsonReader. If any of you happen to be free, do help us trace up further for the culprit that causes this problem
Cheers
Lionel
Looks like to have multi select enabled for you grid. You can configure the selection model of the grid by using the Ext.grid.RowSelectionModel.
Set your selection model to single select by configuring the sm (selection model) in grid panel as show below:
sm: new Ext.grid.RowSelectionModel({singleSelect:true})
Update:
Try reloading the grid using the load method or loadData method of the grid's store. Are you updating the grid on the client side? then maybe you can use loadData method. If you are using to get data from remote.. you can use load method. I use load method to update my grid with new records (after some user actions like add,refresh etc). Or you could simply reload as follows:
grid.getStore().reload();

Resources