I've been all over the place for the last two days trying to find the answer to this, but I can't seem to get it. As far as I can tell, I have everything setup correctly, but it just doesn't want to work.
My app scans QRCode tags and saves them to a mutable array. I need to create a way so the user can remove some of the tag objects from the array, so I'm using a UIPickerView to display the list of tags numbers from which to select the tags to delete.
Here's what I have:
in the .h file
#interface ViewController : UIViewController
UIPickerView *tagPickerView;
NSMutableArray *tagPickerData;
#property (nonatomic, retain) IBOutlet UIPickerView *tagPickerView;
#property (nonatomic, retain) NSMutableArray *tagPickerData;
in the .m file
#synthesize tagPickerView;
#synthesize tagPickerData;
-(void) viewDidLoad{
tagPickerData = [[NSMutableArray alloc]init];
[tagPickerView setDelegate:self];
[tagPickerView setDataSource:self];
}
- (void) dealloc{
[tagPickerView release];
[tagPickerData release];
[super dealloc];
}
- (void) imagePickerController: (UIImagePickerController*) reader
didFinishPickingMediaWithInfo: (NSDictionary*) info
{
[tagPickerData addObject:tagString]; //tagString is the value returned from the QRCode reader
}
#pragma mark -
#pragma mark tagPickerView Data Source Methods
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)tagPickerView{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)tagPickerView
numberOfRowsInComponent:(NSInteger)component{
return [tagPickerData count];
}
#pragma mark tagPickerView Delegate Methods
-(NSString *)pickerView:(UIPickerView *)tagPickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component{
return [self.tagPickerData objectAtIndex:row];
}
(I think that's everything.)
In IB I have dataSource, delegate and tagPickerView set for File's Owner. The UIPickerView is hidden until the delete command is called (in this case, shaking the phone), and then it appears on top of everything.
What I am getting is a blank picker view.
I can verify using NSLog that the array is being populated each time the scan is performed, so I know it's not that I am loading an empty array. I can also verify that the Data Source methods are apparently being read, because I can change the number of components to 2 and it is reflected when the picker comes up -- two spinning wheels. But I don't know how to verify that the Delegate method is working.
I wondered if there's a way to populate the picker on command, like when the phone is shaken, instead of using the delegate... or would it even work that way?
The problem with all the reference materials is that it demonstrates how to use an array that is initialized with objects, i.e.
NSArray *array = [[NSArray alloc] initWithObjects:#"String 1",#"string 2",#string 3",(etc.) ,nil];
But I can't seem to find anything that shows how to load the picker from an array created from information collected by the user. Common sense tells me that it should work the same way, but I have learned to abandon common sense when working with Objective-C.
Is there a UIPickerView expert that can help me out here?
Thanks
Whenever you change the contents of the tagPickerData array, you need to call [tagPickerView reloadAllComponents]. The picker doesn’t have any knowledge of its underlying data—you have to send it that message to let it know that it should call its data-source methods.
Related
Basically what i am trying to do is make a network refresh and fetch objects, store it in a nsmutable array in my app delegate. Then i have a listviewController which uses that mutable array to display data.
Setting nsarray is not working here is the code:
//Appdelegate code called after pulldown to refresh is done on listview:
[ListView setArrayElements:(NSMutableArray*)sortedArray ];
NSLog(#"sortedArray count:%d",sortedArray);
NSLog(#"ListView Array count:%d",[ListView.ArrayElements count]);
Result i get in log : "sortedArray count:12" (which is perfect)&"ListView Array count:0" (this is not the right result)
It's hard to assume without seeing more of your code but how do you define the ArrayElements property? It may not be retaining itself and you may not have initialized it when the ListView object is created.
Let me know if this works;
Make sure ArrayElements is created in your ListView.h like the following:
#property (nonatomic, retain) NSMutableArray *ArrayElements;
Or on -init or -viewDidLoad of your ListView,
self.ArrayElements = [[NSMutableArray alloc] init];
Don't forget to release what you retained:
- (void)dealloc
{
//.....
[ArrayElements release];
[super dealloc];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.mTableView reloadData];
}
use this method
Do not make the UITableView object as a property. Just use tableView object , remove 'self.' portion.
Tell me if it helps!
im new to IOS and Objective-C and the whole MVC paradigm and i'm stuck with the following.
I am working on (replica) Contact app, also available in iphone as build in app. i want to pass data through another view controller and the data is pass (null) :(.
My Question is, How do I transfer the data from one view to another?
As most the answers you got, passing data between one controller and another just means to assign a variable from one controller to the other one.
If you have one controller to list your contacts and another one to show a contact details and the flow is starting from the list and going to detail after selecting a contact, you may assign the contact variable (may be an object from the array that is displayed in your list) and assign it to the detail view controller just before showing this one.
- (void)goToDetailViewControllerForContact:(Contact *)c
{
ContactDetailViewController *detailVC = [[[ContactDetailViewController alloc] init] autorelease];
detailVC.contact = c;
[self.navigationController pushViewController:c animated:YES];
//[self presentModalViewController:detailVC animated:YES]; //in case you don't have a navigation controller
}
On the other hand, if you want to insert a new contact from the detail controller to the list controller, I guess the best approach would be to assign the list controller as a delegate to the detail one, so when a contact is added the delegate is notified and act as expected (insert the contact to the array and reload the table view?).
#protocol ContactDelegate <NSObject>
- (void)contactWasCreated:(Contact *)c;
// - (void)contactWasDeleted:(Contact *)c; //may be useful too...
#end
#interface ContactListViewController : UIViewController <ContactDelegate>
#property (nonatomic, retain) NSArray *contacts;
...
#end
#implementation ContactListViewController
#synthesize contacts;
...
- (void)goToDetailViewControllerForContact:(Contact *)c
{
ContactDetailViewController *detailVC = [[[ContactDetailViewController alloc] init] autorelease];
detailVC.contact = c;
detailVC.delegate = self;
[self.navigationController pushViewController:c animated:YES];
//[self presentModalViewController:detailVC animated:YES]; //in case you don't have a navigation controller
}
- (void)contactWasCreated:(Contact *)c
{
self.contacts = [self.contacts arrayByAddingObject:c]; //I'm not sure this is the correct method signature...
[self reloadContacts]; //may be [self.tableView reloadData];
}
...
#end
#interface ContactDetailViewController : UIViewController
#property (nonatomic, assign) id<ContactDelegate> delegate;
...
#end
#implementation ContactDetailViewController
#synthesize delegate; //remember to don't release it on dealloc as it is an assigned property
...
- (void)createContactAction
{
Contact *c = [[[Contact alloc] init] autorelease];
[c configure];
[self.delegate contactWasCreated:c];
}
...
#end
Technically, you shouldn't!
The whole idea is not for "views" to control what happens to the data.
What you want to do is to pass data between controllers (which I imagine is exactly what you are planning to do anyway).
You can have shared model (an instance of an object that both view controllers would access) keeping the data you want to share,
You can use notifications to pass data (it is best suited for certain cases).
You can write something to disk and read it again later.
You can use NSUserDefaults.
You can use KeyChain.
...
The best way is:
declare the appropriate #property in the second view controller
when you create it, simply set the property with
viewController.property = valueYouWantToPass;
I'm a big fan of delegates and protocols.
And in some occasions use a Singleton pattern.
two ways to pass/share data between view controller
create an object and sent the data like this
QGraduteYr *tableverify=[[QGraduteYr alloc]initWithStyle:UITableViewStyleGrouped];
tableverify.mystring=myString
[self.navigationController pushViewController:tableverify animated:YES];
another method is stor it in the delegates and use it via shared delegates
MedicalAppDelegate *appdelegate=(MedicalAppDelegate *)[[UIApplication sharedApplication]delegate];
appdelegate.collnameStr=collStr;
and ust this appdelegates value whereever you need
I have a bit of a dilemma and I was wondering if the good folk here could lend me their programming expertise. I'll try to be as simple and precise as I possibly can so here goes:
I'm a new IOS developer and have only been learning for a couple of months. I am developing an iPhone application for my dissertation at university. The app is simply a guide for people who wish to develop for the iPhone themselves, consisting of tutorials. It consists of numerous Table Views but there is one thing that has got me stumped.
What im trying to do:
One feature im trying to include in my app is a bookmarks facility, this will be accessible from a tab bar. I want to be able to click on a button from any nib file (tutorial) which adds a string to an existing NSMutableArray. This string will correspond with the name of the tutorial where the IB-Action was performed and after added to the Array I can load the nib file when selecting the row at index path.
The Problem:
I can add any object to the array from within the implementation file that contains the array but cannot figure out how to add it from a different implementation file. The UITable view populates from the array perfectly but adding a new entry is another story.
I'll show you my code but i'll leave out anything that is unrelated.
BookmarksViewController.h
#interface BookmarksViewController : UITableViewController {
NSMutableArray *bookmarksArray;
}
#property (nonatomic, retain) NSMutableArray *bookmarksArray;
#end
BookmarksViewController.m
-(void)viewDidLoad {
bookmarksArray = [[NSMutableArray alloc] init];
NSLog(#"String Added");
[bookmarksArray addObject:#"String"];
[super viewDidLoad];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Configure the cell...
cell.textLabel.text = [bookmarksArray objectAtIndex:indexPath.row];
return cell;
}
-(void)dealloc {
[bookmarksArray release];
[super dealloc];
}
NOW FOR THE SECOND VIEW CONTROLLER
Ch01GettingStarted.h
#interface Ch01GettingStarted : UIViewController {
IBOutlet UIScrollView *ScrollView;
}
-(IBAction) pushChap01Bookmark:(id)sender;
#end
Ch01GettingStarted.m
-(IBAction) pushChap01Bookmark:(id)sender{
BookmarksViewController *bookmarksViewController = [[BookmarksViewController alloc]init];
[bookmarksViewController.bookmarksArray addObject:#"NewString"];
NSLog(#"ADD ENTRY");
[bookmarksViewController.tableView reloadData];
NSLog(#"RELOAD TABLE");
[bookmarksViewController release];
NSLog(#"ADD BOOKMARK RELEASE");
}
BTW - the IB-Action was declared in the header file.
Ah, i originally tried doing this as '[BookmarksViewController.bookmarksArray addObject:#"NewString"];' but I came up with an "expected ':' at '.'" error and I read somewhere that I needed to use an instance variable of BookmarksViewController so i declared it just above the addObject method.
Please be gentle with me as I haven't been doing this for long but this is certainly something that's going to be a big part of my professional future.
Any insight anyone could offer to me would be magical.
Thank you, thank you, thank you.
The problem lies in this method:
-(IBAction) pushChap01Bookmark:(id)sender
You are creating a NEW bookmarksViewController here, by doing this:
BookmarksViewController *bookmarksViewController = [[BookmarksViewController alloc]init];
You don't want to do that because you want to update the current view controller. You need to create a link from Ch01GettingStarted.
Assuming you are using Interface Builder, you could create this link using an IBOutlet. In the Ch01GettingStarted interface, add the following line:
IBOutlet BookmarksViewController *bookmarksViewController;
(Between the brackets)
I think you already know how to link this in Interface Builder.
Then just remove this line:
BookmarksViewController *bookmarksViewController = [[BookmarksViewController alloc]init];
And this line:
[bookmarksViewController release];
And it should work.
Why? The 'bookmarksViewController' variable now references the original object (the one you created in Interface Builder) that is actually displayed.
Just to be sure.
Do you have this method in BookmarksViewController.m?
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
[bookmarksArray count];
}
Because after bookmarksViewController is created. ViewDidLoad method not run yet. bookmarksArray has not been created. So bookmarksArray = nil. You can't add object to nil object. In Second Viewcontroller, You should create bookmarksViewController in loadView method. And addobject in pushChap01Bookmark.
I'm not good English. I hope you can understand it. :D
I really need some more help!
I am trying to pass an array from one view controller to another. I think the latter being a 'child' view controller?
My code is as follows:
MainViewController.h:
#import <UIKit/UIKit.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#interface HelloWorldIOS4ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, AVAudioPlayerDelegate> {
NSMutableArray *countProductCode;
UIPopoverController *detailViewPopover;
}
#property (nonatomic, retain) NSMutableArray *countProductCode;
#property (nonatomic, retain) UIPopoverController *detailViewPopover;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
...
#end
MainViewController.m
#import "HelloWorldIOS4ViewController.h"
#import "JSON.h"
#import "PopoverContentViewController.h"
#implementation HelloWorldIOS4ViewController
#synthesize detailViewPopover;
#synthesize countProductCode;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSDictionary *results = [jsonString JSONValue];
NSLog(#"RETURN: %#", results);
[countProductCode removeAllObjects];
NSArray *products = [results objectForKey:#"items"];
for (NSDictionary *row in products)
{
NSString *code = [row objectForKey:#"ic"];
[countProductCode addObject:code];
}
PopoverContentViewController.countProductCodes = countProductCode;
}
PopoverViewController.h:
#interface PopoverContentViewController : UITableViewController {
NSMutableArray *countProductCodes;
}
#property (nonatomic, retain) NSMutableArray *countProductCodes;
#end
PopoverViewController.m:
#import "PopoverContentViewController.h"
#import "HelloWorldIOS4ViewController.h"
#implementation PopoverContentViewController
#synthesize countProductCodes;
...
I have cut a lot out, but I know from a load of NSLog's dotted around that I get the data back etc, but I cannot pass the array countProductCode to the PopoverViewController's countProductCodes array.
I keep getting
"Accessing unknown 'setCountProductCodes:' class method"
errors.
This may be something really silly that I'm doing but it's driving me crazy!
Can anyone help please?
Thanks
James
Dear James,
I think you would like to have a closer look at the Model-View-Controller paradigm. In your app you are trying to implement some kind of a "super class". Let me explain what that means:
In your MainViewController class which is clearly a controller there is also some part of the model implemented. This is a bad idea, but a very common one to make in the beginning. Maybe I misunderstood your design, but here is how I would implement it:
The Model I would implement a proper model object, which could be in your case as easy as a custom NSObject subclass with a NSMutableArray as a property. In addition this model would have the methods for retrieving data off the internet implemented. That is right: do the networking in the model. You would have to have methods like - (void) refreshProductCode that you would call from your controller. If you want to get really fancy, use an NSOperation to encapsulate the download (then you would use the a synchronous variant of NSURLConnection, because the operation itself is already executed asynchronously) The nice thing would be then if your parsing of the JSON string takes longer, also this is performed in the background and your UI will stay responsive.
So now the model is downloading your stuff - great, but how do I know when it is done? Well you would post a Notification from the model once it is done. What if the download fails? You guessed it right: post a notification that it failed.
The Controller The controller which needs to display data from the model would first to get the model object. In this case the model object is a property on your AppController. The controller then has a property for this model object and retains it, so that the model object does not go away while the controller lives. The controller then also registers for notifications of the model. So how would a typical download work then?
Get an instance of the model object
call -(void) refreshProductCode on the model object
display network activity spinner in status bar and wait for notifications
when the notification came in, on success refresh the UI and on failure restart download or display a note to the user. Also disable the network activity spinner.
How do you shuffle data between view controllers? View controllers should operate a bit like the mafia: every view controller is working on a need-to-know basis. For example if you want a view controller to display the details of your product, you would not pass the model with all your products to the controller. Instead you would have an instance variable on the detail view controller holding only one produce model object, which has all the information like description text, photo etc. The cool thing then is if you ever want to display product information again in you app, you can reuse that view controller, as all it needs is a product model object to do its job.
In your code:
PopoverContentViewController.countProductCodes = countProductCode;
should be:
popoverContentViewController.countProductCodes = countProductCode;
Your instance name should be different from the class name.
In the mainViewController class, in the following method
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
you are accessing the "countProductCodes" using class name. You should access using its object.
like
PopoverContentViewController *obj = [[PopoverContentViewController alloc] init];
obj.countProductCodes = countProductCodes;
In MainViewController.h:
+(NSMutableArray)arrayRes;
In MainViewController.m
+(NSMutableArray)arrayRes{
return countProductCode;
}
perform any of the code changes on countProductCode array as usually
In PopoverViewController.m
declare #class MainViewController; and in viewDidLoad
NSMutableArray *newArray;
newArray = [MainViewController arrayRes];
i'm a bit new to iphone development. I made it to develop a nice App which loads XML Data from a feed, displays this data in a UITableView and if a user taps a row there should be a detail view which displays the data.
Thats where i got stuck a little bit. It's not clear for me how to hand over the data of the entry selected by the user to my detail view. The Detail-View is called via presentModalView...
I thought about:
Calling a "setDetails:(PostingData *)myPosting" function of the viewController of my detail view.
presenting the detailView to the user by calling presentModalViewAnimated.
The view is presented, but the setDetails: function crashes without any output to the debugger console.
MY QUESTION:
What is the right way to hand over Data (in custom objects as instance of a self written class) from my view Controller to a View Controller which should display detail data.
Any hint or help is appreciated. I can't pay you for your help, but i'm on my way becoming better and then helping others too :-).
Method 1: Pass it in custom init method
In your Header File declare a property
#property (nonatomic, retain) id myDataObject;
And in your implementation use a custom init like this
-(id)initCustom:(id)myObject;
if(self = [super init]) {
myDataObject = [myObject retain];
}
return self;
}
Method 2: Use a property
Use #property in your Header
and #synthesize in your .m Implementation File
[CustomUIViewController* newViewController = [[CustomUIViewControlleralloc] init];
newViewConroller.myDataObject = myObject;
[view addSubview:newViewController.view];
[newViewController release];
define the custom object in your class. #property(nonatomic, retain) MyClass * myClass;
load the feed into a NSMutableDictionary and provide that to your class
[YOUR_VIEW_CONTROLLER *yourViewController = [[YOUR_VIEW_CONTROLLER alloc] init];
yourViewController.PROPERTY_DEFINED_BEFORE = yourObject;
[view addSubview:yourViewController.view];
[yourViewController release];
cheers