I was reading the docs to learn how to add a row in a table view, and I found this example :
- (void)save:sender {
UITextField *textField = [(EditableTableViewTextField *)[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]] textField];
SimpleEditableListAppDelegate *controller = (SimpleEditableListAppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *newItem = textField.text;
if (newItem != nil) {
[controller insertObject:newItem inListAtIndex:[controller countOfList]];
}
[self dismissModalViewControllerAnimated:YES];
}
I don't understand the method : insertObject:inListAtIndex: or what [[UIApplication sharedApplication] delegate]; stands for; are we putting the data in a plist file? Could someone explain this to me? The UIApplication docs do not really help.
[[UIApplication sharedApplication] delegate] is the main application delegate, typically this is a class named AppDelegate. The main application delegate is the one that is created on application start-up and which is the main controller for your application.
I'm going to assume that you're using something similar to this class as your AppDelegate class.
[controller insertObject:newItem inListAtIndex:[controller countOfList]];
This assumes that your AppDelegate class has a method named insertObject:inListAtIndex: on it. For the class I linked the method looks like this:
- (void)insertObject:(id)obj inListAtIndex:(NSUInteger)theIndex {
[list insertObject:obj atIndex:theIndex];
}
So in this case, that method is adding the object to a member variable of your AppDelegate class called List.
There is no connection to plist. Just message exchange with help of delegates to change table view.
From apple documentation:
When a table view enters editing mode and when users click an editing control, the table view sends a series of messages to its data source and delegate, but only if they implement these methods. These methods allow the data source and delegate to refine the appearance and behavior of rows in the table view; the messages also enable them to carry out the deletion or insertion operation.
Read this:
http://developer.apple.com/library/ios/#documentation/userexperience/conceptual/TableView_iPhone/ManageInsertDeleteRow/ManageInsertDeleteRow.html
And one of the best books - Beginning iphone 4 (or 5)
Related
I am new to iOS and was trying to wrap my head around the functionality of Appdelegate callback's and field population.
So here's what I am doing:
declaring a global property in Appdelegate's header
#property NSString* GlobalUsername;
Populating the value to this property in one on of the callbacks
- (void)applicationDidBecomeActive:(UIApplication *)application {
_GlobalUsername = #"username";
}
Now in the ViewControler's button click method, I am creating an object of AppDelegate, accessing GlobalUsername and showing it on the screen using textView.
- (IBAction)ShowDetails:(id)sender
{
AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSString* details=[NSString stringWithFormat:#"Username is : %#",appdelegate.GlobalUsername];
[_textView setText:details];
}
Now what I don't understand is that how is the value of GlobalUsername is being associated with the object of AppDelegate. GlobalUsername was populated when application became active and not when I created an instance of the Appdelegate so how does the object returned by sharedApplication method still has this information encapsulated with it?
delegate of UIApplication works according to singleton pattern. There is only 1 instance every time you try to get it. For example:
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
...Any API requests...
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
If UIApplication's delegate will be different every time it won't save state of network indicator.
To start I am building an app to learn the basics of Objective-C. If there is anything unclear please let me know and I will edit my question.
The app is supposed to have the next functionality.
Open the camera preview when the app is executed. On the top there is a button to go to a TemplateController where the user can select an array of frames to select from a UICollectionView. User selects the Template and returns to the Camera Preview. User takes a picture and the picture with the frame selected is shown in the PreviewController. If the user doesn't like the frame and wants to switch it for another one. PreviewController has button on top to go to the TemplateController, select the frame and go back again to the PreviewController with the new frame.
I do not want to create an object for the frame everytime. I want the AppDelegate to hold that object. To keep it alive per say?(sorry, English is not my mother tongue).
I was thinking to use NSUserDefaults BUT I really want to do it using the AppDelegate. So at this point NSUserDefaults is not an option.
Now, I am using storyboards with a navigation controller. A screenshot is available here
Right now when I pass from the TemplateController to my PreviewController my code looks like this:
Reaching TemplateController from MainController or PreviewController
- (IBAction)showFrameSelector:(id)sender
{
UIStoryboard *storyboard;
storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
TemplateController *templateController = [storyboard instantiateViewControllerWithIdentifier:#"TemplateController"];
templateController.frameDelegate = self;
[self presentViewController:templateController animated:YES completion:nil];
}
Passing the data from TemplateController to its controller's destiny (Either MainController or PreviewController)
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
_selectedLabelStr = [self.frameImages[indexPath.section] objectAtIndex:indexPath.row];
[self.collectionView deselectItemAtIndexPath:indexPath animated:NO];
[self dismissViewControllerAnimated:YES completion:^{
if ([self.frameDelegate respondsToSelector:#selector(templateControllerLoadFrame:)])
{
[self.frameDelegate performSelector:#selector(templateControllerLoadFrame:) withObject:self];
}
}];
}
This loads the selected frame in PreviewController
- (void)templateControllerLoadFrame:(TemplateController *)sender
{
UIImage *tmp = [UIImage imageNamed:sender.selectedLabelStr];
_frameImageView.image = tmp;
}
My problem is, I don't have very clear what changes I have to do on the AppDelegate(it is untouched right now). What would be the best approach to accomplish this?
Main issue is when Tamplate is chosen before taking the still image. If I select the frame after taking the picture then it displays.
I am not certain that I understand your question. Stuffing an object into the app delegate solution may not be the best way forward. In fact I believe you ought to look at the delegation pattern that is used by Apple to communicate between view controllers. Please note that you appear to be doing half of the delegate pattern already. For example you make your PreviewController a frameDelegate of the TemplateController.
So I would think you'd have something like the following to transfer information from TemplateController back to the PreviewController. Note that I've included prepare for segue as that is a common pattern to push a data object forward (it will be called if you connect a segue from the PreviewController to the TemplateController and in your action method call performSegueWithIdentifier:#"SegueTitle"). Use of the "templateControllerDidFinish" delegation method is a common pattern used to push information back from TemplateController when it closes.
TemplateController.h
#class TemplateController;
#protocol TemplateControllerDelegate <NSObject>
-(void) templateControllerDidFinish :(TemplateController*)controller;
#end
#interface TemplateController : UIViewController
#property (nonatomic, weak) id <TemplateControllerDelegate>delegate;
...
#end
TemplateController.m
//! The internals for this method can also be called from wherever in your code you need to dismiss the TemplateController by copying the internal
-(IBAction)doneButtonAction:(id)sender
{
__weak TemplateController*weakSelf = self;
[self dismissViewControllerAnimated:YES completion:^{
[self.delegate templateControllerDidFinish:weakSelf];
}];
}
PreviewController.h
#import "TemplateController.h"
#interface PreviewController<TemplateControllerDelegate>
...
#end
PreviewController.m
#implementation
...
-(void) templateControllerDidFinish :(TemplateController*)controller
{
self.dataProperty = controller.someImportantData;
...
}
...
-(void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
if ( [[segue identifier]isEqualToString:#""] )
{
TemplateController *tc = [segue destinationViewController];
tc.delegate = self;
tc.data = [someDataObjectFromPreviewController];
}
}
To fix this situation a bit more:
Add a segue from the PreviewController to the TemplateController
(Ctrl-drag from Preview view controller to the Template Controller
in the document outline mode)
Name the segue identifier in the identity inspector
Change your code that presents the view controller from:
(IBAction)showFrameSelector:(id)sender
{
UIStoryboard *storyboard;
storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
TemplateController *templateController = [storyboard instantiateViewControllerWithIdentifier:#"TemplateController"];
templateController.frameDelegate = self;
[self presentViewController:templateController animated:YES completion:nil];
}
to
- (IBAction)showFrameSelector:(id)sender
{
[self performSegueWithIdentifier:#"SegueTitle"];
}
Add your data object to the target view controller as noted in prepareForSegue and you will be in good shape. Then use the delegate method to catch any data returned from your template (just add the data as properties to the controller and you should be golden)
You can see a better example of this delegation in a utility project template from Xcode (I just keyed this in..) I hope this information helps. You can get more information at these resources and also by searching Google and SO for iOS delegation :
Concepts in Objective C (Delegates and Data Sources)
Cocoa Core Competencies
I'm having an issue with regards to creating a separate Controller class for an NSOutlineView I have.
I've created a new class named LTSidebarViewController and in my MainMenu.xib file I've added an Object to the 'workbench' and linked it to my LTSidebarViewController class. I've also set the delegate and datasource to be linked to the NSOutlineView in MainMenu.xib.
What I am looking to do is create an instance of this class from within - (void)applicationDidFinishLaunching:(NSNotification *)aNotification in my AppDelegate file and when I do so I want to pass in the App Delegate's managedObjectContext. So, I've created a custom init method in LTSidebarViewController which looks like so:
-(id)initWithManagedObject:(NSManagedObjectContext*)managedObject{
self = [super init];
if (self) {
self.managedObjectContext = managedObject;
NSFetchRequest *subjectsFetchReq = [[NSFetchRequest alloc]init];
[subjectsFetchReq setEntity:[NSEntityDescription entityForName:#"Subject"
inManagedObjectContext:self.managedObjectContext]];
subjectsArray = [self.managedObjectContext executeFetchRequest:subjectsFetchReq error:nil];
_topLevelItems = [NSArray arrayWithObjects:#"SUBJECTS", nil];
// The data is stored in a dictionary
_childrenDictionary = [NSMutableDictionary new];
[_childrenDictionary setObject:subjectsArray forKey:#"SUBJECTS"];
// The basic recipe for a sidebar
[_sidebarOutlineView sizeLastColumnToFit];
[_sidebarOutlineView reloadData];
[_sidebarOutlineView setFloatsGroupRows:NO];
// Set the row size of the tableview
[_sidebarOutlineView setRowSizeStyle:NSTableViewRowSizeStyleLarge];
// Expand all the root items; disable the expansion animation that normally happens
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0];
[_sidebarOutlineView expandItem:nil expandChildren:YES];
[NSAnimationContext endGrouping];
// Automatically select first row
[_sidebarOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
}
return self;
}
I also have all the required methods in this class, - (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item etc.
Inside the - (void)applicationDidFinishLaunching:(NSNotification *)aNotification method in the App Delegate, I have the following:
LTSidebarViewController *sidebarViewController = [[LTSidebarViewController alloc] initWithManagedObject:self.managedObjectContext];
My problem is that this isn't working, I don't get any errors and the app runs but no data is displayed in the NSOutlineView.
Now from what I can tell the problem is that when the MainMenu.xib file is initially loaded, it's automatically creating an instance of my LTSidebarViewController class and calling it's init method but because my init method isn't doing anything the app doesn't finish launching correctly.
Am i taking the correct approach here? In simple terms all I'm looking for is to have a separate file that is used as the datasource for my NSOutlineView.
When working with NSOutlineView I generally put in extreme amounts of logging to figure out what's going on. I would probably do something like the following (maybe you have already done some of this):
Make sure you really have data in subjectsArray by logging it, e.g.
NSLog(#"subjectsArray");
NSLog(#"%#", subjectsArray);
Make sure you have implemented the NSOutlineView Datasource protocol methods from NSOutlineView Datasource Methods in your AppDelegate.m file and that they're returning the appropriate data.
If you need help implementing these, try a tutorial such as Source Lists and NSOutlineView.
I usually wind up with NSLog statements in each of the NSOutlineView data source methods to make sure they are being called and that I understand what each is expecting and returning.
Make sure your delegate and datasource are not nil for some reason in your initWithManagedObject:(NSManagedObjectContext *)managedObject method by logging them, e.g.
NSLog(#"datasource: %#", [self datasource]);
NSLog(#"delegate: %#", [self delegate]);
If you find that for some reason they are nil, you could manually set them just to make sure that's not the problem, e.g. in initWithManagedObject:
[self setDelegate: [NSApp delegate]];
[self setDatasource: [NSApp delegate]];
As far as whether this is the "correct" approach: I'm not clear from your code whether you're intending that the sideBarController is both the delegate and the datasource or whether the AppDelegate is serving those roles. Obviously, you'll need to implement the delegate and datasource protocols in the appropriate files. You certain can have AppDelegate serve those roles, although it seems to make more sense to have your sideBarController do that.
A small note: I sometimes access AppDelegate's managedObjectContext directly from supporting files with something like
-(NSManagedObjectContext *)managedObjectContext
{
return [[NSApp delegate] managedObjectContext];
}
rather than passing the managedObjectContext in manually to every file.
I have a textview on my viewcontroller.xib and I have a method to modify the text. I want to access that textview method on viewcontroller.m from a created object. I have a class called person that has in the init method to call the viewcontroller's method to update the text. Basically I want the textview to say "Person Created".
Here is the code I use inside my -init method of the person class which does not work:
ViewController * Viewscreen = (ViewController *) [[UIApplication sharedApplication] delegate];
[Viewscreen UpdateScreenText:#"Person Created!"];
ViewController * Viewscreen = (ViewController *) [[UIApplication sharedApplication] delegate];
This looks very strange to me, is your App Delegate really named ViewController? Are you trying to access a property declared in your App Delegate called Viewscreen?
If so then you would want to use the following to get a reference to it
ViewController * Viewscreen = [(ViewController *) [[UIApplication sharedApplication] delegate] Viewscreen];
Please clarify some more and we may be able to better assist.
I made a book for children. Every page is a ViewController. In the beginning, I did all the switching of the ViewControllers in the AppDelegate, but after having troubles with AutoRotation i did all the switching in another ViewController called ViewControllerSwitch.
Before, the code was like this. In the AppDelegate:
- (void)goToNextPage3 {
self.view3 = [[[ViewController3 alloc] init] autorelease];
view3.view.frame = CGRectMake(769, 0, 768, 1024);
[window addSubview:view3.view];
[UIView …SomeAnimationStuff...];
[UIView setAnimationDidStopSelector:#selector(animationDidStop3:finished:context:)];
}
- (void)animationDidStop3:(NSString *)animationID finished:(NSNumber *)finished context: (void *)context {
[self.view1a.view removeFromSuperview];
self.view1a = nil;
}
And here is the code from one of my view controllers ("pages") called ViewController1a:
- (void)buttonClicked {
MyBookAppDelegate* next2 =(MyBookAppDelegate *) [[UIApplication sharedApplication] delegate];
[next2 goToNextPage3];
}
This worked like a charm.
Now all my switching is in ViewControllerSwitch. How should I change the code in ViewController1a to access goToNextPage3?
I tried this:
- (void)buttonClicked {
ViewControllerSwitch* next2 = (ViewControllerSwitch *) [[UIApplication sharedApplication] delegate];
[next2 goToNextPage3];
}
It gives me a SIGABRT at [next2 goToNextPage3].
Any ideas?
Update:
i am still trying, so now i did this:
in my Viewcontroller1a.h:
#import <UIKit/UIKit.h>
#import "ViewControllerSwitch.h"
#class ViewController2;
#class ViewControllerSwitch;
#protocol ViewController1Delegate;
#interface ViewController1a : UIViewController
{
id<ViewController1aDelegate>myDelegate;
}
#property(nonatomic, assign)id<ViewController1Delegate>myDelegate;
#end
#protocol ViewController1Delegate
-(void)goToNextPageV;
#end
and in my .m file:
- (void)buttonClicked {
[self.myDelegate goToNextPageV];
}
i know there is something missing in the ViewControllerSwitch but i don´t know what.
As LordTwaroog said, this line is returning an object (your app delegate) of type MyBookAppDelegate:
[[UIApplication sharedApplication] delegate];
Then you're attempting to cast it to be an object of type ViewControllerSwitch with this:
(ViewControllerSwitch *)
This is incorrect. Your app delegate is an NSObject subclass conforming to the UIApplicationDelegate protocol. It's not a view controller.
The correct code might look like this:
- (void)buttonClicked {
ViewControllerSwitch* next2 = [ViewControllerSwitch alloc] initWithNibName:#"ViewControllerSwitch" bundle:nil]
[next2 goToNextPage3];
}
But depending on your app structure this might not be what you want. This is creating a brand new object of type ViewControllerSwitch. It's not returning the possibly-already-existing other ViewControllerSwitch object.
When you were using the app delegate to perform the switching, you had the benefit of being able to retrieve the existing app delegate object (rather than retrieving a newly created object of it's type). The app delegate is a singleton object, easily retrieved by calling [[UIApplication sharedApplication] delegate]. However, your ViewControllerSwitch object might not be set up as a singleton. So your access to it will depend on your object ownership structure. We'd have to see more of your code to help you with that.
[[UIApplication sharedApplication] delegate] still returns MyBookAppDelegate. Why do you try to get ViewControllerSwitch object out of it? You should use your ViewControllerSwitch object. Can you provide more code details on this object?
Possible solutions (if I understand you well):
Put your ViewControllerSwitch as an object in AppDelegate, so you could use:
ViewControllerSwitch *switch = [[[UIApplication sharedApplication] delegate] viewControllerSwitch]
Each ViewController can have a reference to your ViewControllerSwitch
Each ViewController should have a delegate with protocol (e.g.) ViewControllerDelegate, which will have a method to perform switching. Then after setting the delegate to appropriate object, you'll be able to switch your pages