Add object to an NSArray from UITableViewCell - ios

I have created a customed UITableViewCell that contains a PFObject property and a button. I want the button to add this object on a NSMutableArray and pass this Array to another UIViewController. The problem is that I can't implement the prepareForSegue method into custom UITableViewCell so when I display the array in restoCardConfirmationVC I always get an empty array.
This is my code :
#import "boxTableViewCell.h"
#import "RestoCardConfirmationViewController.h"
#import "RestauCardViewController.h"
#implementation boxTableViewCell {
NSMutableArray *_pickerPlace;
RestauCardViewController *_restoCardVC;
RestoCardConfirmationViewController *_restoCardConfirmationVC;
}
- (void)awakeFromNib {
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return _pickerPlace.count;
}
- (NSString*)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return [_pickerPlace objectAtIndex:row];
}
- (IBAction)select:(id)sender {
NSLog(#"the select box is : %#",_box);
[_restoCardConfirmationVC.boxesCommande addObject:_box];
self.select.enabled = NO;
}
#end
RestaCardConfirmation.h
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#interface RestoCardConfirmationViewController : UIViewController
#property(nonatomic) PFObject *commande;
#property(nonatomic) NSMutableArray *boxesCommande;
- (IBAction)confirmer:(id)sender;
#end

You should initialise the array before using it. Like this
- (void)awakeFromNib {
// Initialization code
_restoCardConfirmationVC = [[RestoCardConfirmationViewController alloc] init];
_restoCardConfirmationVC.boxesCommande = [NSMutableArray array];
}
Although this is a bad approach. You should initialise the array in the RestoCardConfirmationViewController initialisation.

You have not initialised the Array in which you are adding object. Allocate it either in awakeFromNib or in ViewDidLoad and then add an object in it.
Moreover, I don't find a good reason to alloc view controller in awakeFromNib of TableViewCell. You should allocate the VC only when Button is Tapped.
Hence the code should be like:
- (IBAction)select:(id)sender {
_restoCardConfirmationVC = [[RestoCardConfirmationViewController alloc] init];
[_restoCardConfirmationVC view]; //This will call the ViewDidLoad in Advance
[_restoCardConfirmationVC.boxesCommande addObject:_box];
self.select.enabled = NO;
}
And in ViewDidLoad of RestoCardConfirmationViewController:
- (void)viewDidLoad {
[super viewDidLoad];
_boxesCommande = [NSMutableArray array];
//Other initialisation code
}

Related

Get the UITextfield textFieldDidEndEditing callback in a custom UIPicker class

According to my requirement I need a UIPicker in multiple sections of my app with the same list. So I'm creating a custom class of UIPicker and so far i didn't receive any problem but one thing i would like improve is currently I'm setting the value from the textfield delegate method lets take a example :-
MembershipPickerView.h
#interface MembershipPickerview : UIPickerView <UIPickerViewDelegate,UIPickerViewDataSource>
#property (strong, nonatomic) NSArray <Membership *> *membershipData;
#end
MembershipPickerView.m
#implementation MembershipPickerview{
NSArray <Membership *> *membershipData;
}
-(instancetype)init{
self = [super init];
if (self) {
self.dataSource = self;
self.delegate = self;
[self updateDataSource];
}
return self;
}
- (void)updateDataSource{
Membership *m1 = [Membership new];
m1.mName = #"M1";
m1.mId = #"1";
Membership *m2 = [Membership new];
m1.mName = #"M2";
m1.mId = #"2";
membershipData = #[m1,m2];
[self reloadAllComponents];
}
- (NSInteger)numberOfComponentsInPickerView:(nonnull UIPickerView *)pickerView {
return 1;
}
- (NSInteger)pickerView:(nonnull UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return membershipData.count;
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
return membershipData[row].mName;
}
#end
Have a view controller lets say vc1 that have a UITextfield in it named textField1
In my "vc1.m"
#implementation vc1 {
UITextField *textField1;
MembershipPickerview *picker;
}
-(void)viewDidLoad {
picker = [MembershipPickerview new];
textField1.inputView = picker;
textField1.delegate = self;
}
-(void)textFieldDidEndEditing:(UITextField *)textField{
NSInteger index = [picker selectedRowInComponent:0];
NSLog(#"%#",picker.membershipData[index].mName)
}
#end
My Questions
Can i achieve the functionality that I'm doing in the textFieldDidEndEditing within the class and without setting the delegate of textfield to picker? If yes, How?
Is it possible to get an event (textfield had resigned) in the Picker class without writing extra code in the other view controllers

Pass an object defined from a customed tableViewCell

To explain my situation, I have a tableView with custom tableViewCell and I have created a button in this customed cell. I want this button to add an object box which is already defined to an NSMutableArray and pass this array to restoCardConfirmationViewController in order to create a cart.
The problem is that when I try to display the NSMutablearray in restoCardConfirmationViewController, the result is nil. This is my code :
The boxTableViewCell :
#import "boxTableViewCell.h"
#import "RestoCardConfirmationViewController.h"
#import "RestauCardViewController.h"
#implementation boxTableViewCell {
NSMutableArray *_pickerPlace;
}
- (IBAction)select:(id)sender {
RestoCardConfirmationViewController *restauCardConfirmation = [[RestoCardConfirmationViewController alloc] init];
[restauCardConfirmation.boxesCommande addObject:_box];
NSLog(#"la box choisie est %#",_box);
self.select.enabled = NO;
if(!self.select.enabled){
NSLog(#"button desabled");
}
}
The restoCardConfirmationViewController.h :
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#interface RestoCardConfirmationViewController : UIViewController
#property(nonatomic) NSMutableArray *boxesCommande;
The restoCardConfirmationViewController.m :
#import "RestoCardConfirmationViewController.h"
#import "ChoisirDateViewController.h"
#interface RestoCardConfirmationViewController ()
#end
#implementation RestoCardConfirmationViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"The NSMutableArray in the other %#",self.boxesCommande);
// Do any additional setup after loading the view.
}
One approach is Delegation. Create a delegate that notifies every object added to RestoCardConfirmationViewController.
And the other simpler method is :
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.purchases count];
}
In the MainViewViewController, add button click event instead of using it in tableview cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
cell.button.tag = indexPath.row;
[cell.button addTarget:self
action:#selector(select)
forControlEvents:UIControlEventTouchUpInside];
// ...
}
This calls the following method, also implemented in MainViewController
- (IBAction)select:(id)sender {
int index = ((UIButton *)sender).tag;//it returns the indexPath
[self.boxesArray addObject: self.purchases[index]];
NSLog(#"la box choisie est %#",_box);
self.select.enabled = NO;
if(!self.select.enabled){
NSLog(#"button desabled");
}
}
Now in prepareforSeque, pass the created array to RestoCardConfirmationViewController.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"YOUR_SEGUE_NAME_HERE"])
{
// Get reference to the destination view controller
RestoCardConfirmationViewController *vc = [segue destinationViewController];
// Pass any objects to the view controller here, like...
vc.boxesCommande = self.boxesArray;
}
}
Push To ViewController After addObject :
- (IBAction)select:(id)sender
{
RestoCardConfirmationViewController *restauCardConfirmation = [[RestoCardConfirmationViewController alloc] init];
[restauCardConfirmation.boxesCommande addObject:_box];
NSLog(#"la box choisie est %#",_box);
self.select.enabled = NO;
if(!self.select.enabled)
{
NSLog(#"button desabled");
}
[self.navigationController pushViewController:restauCardConfirmation animated:YES];
}
Don't forget to initialize your boxesCommande array in your custom view controller.
Add the following lines to your RestoCardConfirmationViewController.m in the #implementation:
- (instancetype)init {
if (self = [super init]) {
self.boxesCommande = [[NSMutableArray alloc] init];
}
return self;
}

EXC_BAD_ACCESS on UIPickerView - I need a bit of direction on why

I get the exc_bad_access error when running my project and trying to change the picker.
The error is occurring on
- (NSString*)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component
Below is my code. From reading the other SO articles I realize I am probably not retaining my variable. I'm new and learning and appreciate the help.
#import <UIKit/UIKit.h>
#import "RootViewController.h"
#class RootViewController;
#interface AddConditionViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate> {
IBOutlet UITextField *txtConditionDetail;
IBOutlet UITextField *txtConditionArea;
IBOutlet UIPickerView *conditionNamesPicker;
NSMutableArray *names;
NSMutableArray *conditionDefs;
RootViewController *rvc;
NSString *conditionName;
}
#property (retain, nonatomic) IBOutlet UIPickerView *conditionNamesPicker;
#property (nonatomic,assign) RootViewController *rvc;
#property (nonatomic, retain) NSString *conditionName;
#property (nonatomic,assign) NSMutableArray *names;
#property (nonatomic,assign) NSMutableArray *conditionDefs;
#end
#import "AddConditionViewController.h"
#import "ConditionsAppDelegate.h"
#import "Condition.h"
#import "ConditionDef.h"
#import "Formula.h"
#implementation AddConditionViewController
#synthesize rvc, conditionNamesPicker, names, conditionDefs, conditionName;
/*
// Implement loadView to create a view hierarchy programmatically.
- (void)loadView {
}
*/
// Implement viewDidLoad to do additional setup after loading the view.
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"Add Condition";
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self action:#selector(cancel_Clicked:)] autorelease];
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemSave
target:self action:#selector(save_Clicked:)] autorelease];
self.view.backgroundColor = [UIColor groupTableViewBackgroundColor];
ConditionsAppDelegate *appDelegate = (ConditionsAppDelegate *)[[UIApplication sharedApplication] delegate];
conditionDefs = appDelegate.getConditionDefs;
self.names = [NSMutableArray arrayWithCapacity:[conditionDefs count]];
for (ConditionDef *def in conditionDefs) {
NSString *condition_name = def.condition_name;
if (!condition_name) {
condition_name = #"<Unknown Account>";
}
[names addObject:condition_name];
}
self.conditionNamesPicker.dataSource = self;
self.conditionNamesPicker.delegate = self;
NSLog(#"LINE 48");
}
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
//Set the textboxes to empty string.
txtConditionArea.text = #"";
txtConditionDetail.text = #"";
//Make the Category name textfield to be the first responder.
[txtConditionArea becomeFirstResponder];
NSLog(#"LINE 63");
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
// The number of columns of data
- (int)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
// The number of rows of data
- (int)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
NSLog(#" LINE 87 - COUNT OF CONDITION DEFS TO SHOW = %i", names.count);
[conditionNamesPicker setDataSource:self];
return [names count];
}
// The data to return for the row and component (column) that's being passed in
- (NSString*)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component
{
// NSLog(#"LINE 94 - here is the bug: conditionDefs[row] %#", names[row]);
return names[row];
}
// Catpure the picker view selection
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
// This method is triggered whenever the user makes a change to the picker selection.
// The parameter named row and component represents what was selected.
conditionName = names[row];
}
- (void) save_Clicked:(id)sender {
ConditionsAppDelegate *appDelegate = (ConditionsAppDelegate *)[[UIApplication sharedApplication] delegate];
//Create a Condition Object.
Condition *c = [[Condition alloc] init];
NSInteger newId = c.getNextConditionId;
Condition *cond = [[Condition alloc] initWithPrimaryKey:newId];
cond.condition_area = txtConditionArea.text;
cond.condition_detail = txtConditionDetail.text;
cond.condition_name = conditionName;
//Add the object
// [appDelegate addCondition:cond];
[appDelegate populateFromDatabase];
// ADD TO THE ARRAY:
// [cvc.categories addObject:cond];
// [cvc.Conditions addObject:cond];
rvc.Conditions = [appDelegate activeConditions];
// UPDATE THE TABLEVIEW
[rvc.tableView reloadData];
// release
[cond release];
[c release];
//Dismiss the controller.
[self.navigationController dismissViewControllerAnimated:YES completion: nil];
}
- (void) cancel_Clicked:(id)sender {
//Dismiss the controller.
[self.navigationController dismissViewControllerAnimated:YES completion: nil];
}
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
NSLog(#"LINE 159");
[theTextField resignFirstResponder];
return YES;
}
- (void)dealloc {
[txtConditionArea release];
[txtConditionDetail release];
[conditionNamesPicker release];
[super dealloc];
}
#end
Please change the property assign toretain for names property. And other array or those object inheriting fromNSObject. It is an object and you are keeping it as assign property. Use assign only for primitive data type. Try this and let me know.
You are setting the data source of PickerView in one the DataSource methods. This is not valid. Remove the below line
[conditionNamesPicker setDataSource:self];
from function:
numberOfRowsInComponent:
You are setting it again.

Terminating app due to uncaught exception 'NSInternalInconsistencyException? any clue?

here's my code that I am using now but still getting all kinds of errors:
No visible #interface for 'HomeViewController' declares the selector 'getCount'
And
/Volumes/Lex/HomeViewController.h:12:12: Required for direct or indirect protocol 'UIPickerViewDataSource'
/Volumes/Lexar/HomeViewController.m:15:17: Incomplete implementation
My code (.m file)
- (void)viewDidLoad
{
[super viewDidLoad];
PFUser *currentUser = [PFUser currentUser];
if (currentUser) {
NSLog(#"Current user: %#" , currentUser.username);
}
else {
[self performSegueWithIdentifier:#"showLogin" sender:self];
self.pickerView.dataSource = self;
self.pickerView.delegate = self;
}
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent: (NSInteger)component
{
if ([self getCount] == 0)
return 1;
return [self getCount];
}
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent: (NSInteger)component reusingView:(UIView *)view {
if ([self getCount] == 0)
return nil;
}
- (IBAction)logout:(id)sender {
[PFUser logOut];
[self performSegueWithIdentifier:#"showLogin" sender:self];
}
#end
And the header
/// .h controller
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#interface HomeViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>
#property (strong, nonatomic) IBOutlet UIPickerView *pickerView;
- (IBAction)logout:(id)sender;
#end
As you mentioned in your comment, you are just placing UIPickerView...but for using picker view you need to set Datasource like UITableView and have to implement all #required methods.
In picker view's data source protocol, there are 2 #required methods
// returns the number of 'columns' to display.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView;
// returns the # of rows in each component..
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component;
So you need to implement above two method in your controller (say MainViewController). and don't forget to set this class as delegate and datasource to UIPicker view as below
in ViewDidLoad of MainViewController
self.yourPickerView.datasource = self;
self.yourPickerView.delegate = self;

Reloaddata not call cellForRowatIndexPath

I create a UITableViewController using storyboard, and want to reload the tableview every time it detect a device(for bonjour protocol).
However, the reload data method only calls numberofRows, but not call CellForRowatIndexPath. So I have nothing changed. The mutablearray I checked is always going correctly. NewObjects are added. But the tableview doesn't change with the array. So I am wondering if I set sth wrong here.
From the NSLog I added, I found, I could load the tableview at the beginning with
the initialized array
self.serviceArray = [[NSMutableArray alloc]initWithObjects:#"test", nil];
But in the method
- (void)addService:(NSNetService *)....
Everytime I reload, the new object can be added to the mutableArray, but the tableview doesn't change with the array. (It called the numberOfRows, and I check the array size returned is not 0, but then it doesn't call the cellOfRowAtIndex)
Here is my code
.h file
#import <UIKit/UIKit.h>
#import "Server.h"
#interface BrowserViewController : UITableViewController <ServerDelegate>
{
Server *_server;
NSMutableArray *_serviceArray;
}
#property (retain,nonatomic) NSMutableArray *serviceArray;
#property (nonatomic,retain) Server *server;
- (void)addService:(NSNetService *)service moreComing:(BOOL)more;
#end
.m file
#import "BrowserViewController.h"
#implementation BrowserViewController
#synthesize serviceArray = _serviceArray;
#synthesize server = _server;
- (void) dealloc
{
[self.serviceArray release];
[self.tableView release];
[super dealloc];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
self.title = #"Service Browser";
_serviceArray = nil;
self.serviceArray = nil;
[self.tableView setDelegate: self];
[self.tableView setDataSource:self];
//actually I set this in the storyboard already
[self.tableView reloadData];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
_serviceArray = nil;
NSLog(#"viewwilldisappear");
}
- (NSMutableArray *)serviceArray {
if(nil == _serviceArray) {
self.serviceArray = [[NSMutableArray alloc]initWithObjects:#"test", nil];
}
else
{
NSLog(#"update array");
}
return _serviceArray;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)addService:(NSNetService *)service moreComing:(BOOL)more{
[self.serviceArray addObject:service];
if (!more) {
[self.tableView reloadData];
}
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return #"Connection Choices";
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.serviceArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *Cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (Cell == nil) {
Cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]autorelease];
}
NSString *text = [self.serviceArray objectAtIndex:indexPath.row];
Cell.textLabel.text = text;
return Cell;
}
#end
I checked some solution, it's either delegate setting problem, or the array is null. or cell initialized without space. Is there any way to check what's the delegate of some method ?
I also tried to create a tableview property in the .h file, linked IBOutlet, then synthesize it in the .m. But still doesn't work. Could anyone help me?
Since a lot of tutorials are based on window-based application, could someone maybe provide me some tutorial using storyboard and using reloadData ? I could check if the Outlets are wrong.
Thanks in advance.
UPDATE1
AddService method is called in the appDelegate
appDelegate.h
#import <UIKit/UIKit.h>
#import "Server.h"
#class BrowserViewController;
#interface iphoneNetworkAppDelegate : NSObject <UIApplicationDelegate,UITableViewDataSource, UITableViewDelegate, ServerDelegate>
{
Server *_server;
UIWindow *window;
IBOutlet BrowserViewController *BrowserVC;
}
#property (strong, nonatomic) UIWindow *window;
#end
appDelegate.m
#import "iphoneNetworkAppDelegate.h"
#import "BrowserViewController.h"
#implementation iphoneNetworkAppDelegate
#synthesize window;
- (void)dealloc
{
[window release];
[_server release];
_server = nil;
[BrowserVC release];
BrowserVC = nil;
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSString *type = #"TestingProtocol";
_server = [[Server alloc] initWithProtocol:type];
_server.delegate = self;
NSError *error = nil;
if(![_server start:&error]) {
NSLog(#"error = %#", error);
}
BrowserVC = [BrowserViewController alloc];
if (BrowserVC == nil) {
NSLog(#"need to init");
}
BrowserVC.server = _server;
return YES;
}
#pragma mark Server Delegate Methods
- (void)serverRemoteConnectionComplete:(Server *)server {
NSLog(#"Server Started");
BrowserVC.server = server;
}
- (void)serverStopped:(Server *)server {
NSLog(#"Server stopped");
}
- (void)server:(Server *)server didNotStart:(NSDictionary *)errorDict {
NSLog(#"Server did not start %#", errorDict);
}
- (void)server:(Server *)server didAcceptData:(NSData *)data {
NSLog(#"Server did accept data %#", data);
}
- (void)server:(Server *)server lostConnection:(NSDictionary *)errorDict {
NSLog(#"Server lost connection %#", errorDict);
}
- (void)serviceAdded:(NSNetService *)service moreComing:(BOOL)more {
NSLog(#"service added in delegate");
[BrowserVC addService:service moreComing:more];
}
#pragma mark -
- (void)applicationWillTerminate:(UIApplication *)application {
[_server stop];
[_server stopBrowser];
}
If the appDelegate setting is like this, reloadData cannot be called ?
Please check this terms may help you:
self.tableView is object bind in TableView in XIB.
Included <UITableViewDelegate,UITableViewDataSource> delegates in .h file?
Properly set getter setter your object "self.tableView"
Delegate set?
a)[self.tableView setDelegate: self];
b)[self.tableView setDataSource:self];
i think No need for the following lines in view will appear
_serviceArray = nil;
self.serviceArray = nil;
And also makes sure that the function addService got called:

Resources