Variables in different navigation stack - objective-c

Sorry if my question does not match the title, but i think it does:
I have an app with a navigation controller and 3 views. It's an app where you connect to a server and talk with friends.
The thing is, my rootViewController, the login screen, calls the second view witch is the friends list. clicking in a friend on the list takes you to the third view, the chat screen itself.
I wanted to, when the user logs in (rootview), to store the user name used in a variable, so i could use it on the third view, the chat screen, so when he sends a message it can have his name on it, and while retrieving info from server, i can use his name as a parameter too.
By the way, is SQLite the best way to save messages and users? I am afraid of Core Data =/

To keep things loosely coupled I would consider just passing along the userName when instantiating each UIViewController sub class. (Makes unit testing easier as well)
e.g.
// LoginViewController -> user logs in
FriendsViewController *friendsViewController = [[FriendsViewController alloc] initWithUserName:userName];
[self.navigationController pushViewController:friendsViewController];
[friendsViewController release]; friendsViewController = nil;
// FriendsViewController -> user selects a friend
ChatViewController *chatViewController = [[ChatViewController alloc] initWithUserName:userName];
[self.navigationController pushViewController:chatViewController];
[chatViewController release]; chatViewController = nil;
Don't be afraid of core data there are plenty of excellent books out there.

Core data is not that bad. This is the guide I used to get started with Core Data. Once you wrap your head around how everything works it really isn't that bad.
If you wanted to you could use SQLite, but it's a lot of code. Core Data is much simpler to use.

Related

Is It Possible To Customise CNContactPickerViewController?

I'm opening a CNContactPickerViewController for use in an application, but I want to alter the way it presents to suit my apps needs, preferably without rolling my own and doing a lot of re-inventing of the wheel. This is how I am opening it using Objective-C ....
self.contactPicker = [[CNContactPickerViewController alloc] init];
self.contactPicker.delegate = self;
//Only enable contacts to be selected that have atleast one email address
NSArray *propertyKeys = #[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactEmailAddressesKey];
NSPredicate *enablePredicate = [NSPredicate predicateWithFormat:#"emailAddresses.#count != 0"];
self.contactPicker.displayedPropertyKeys = propertyKeys;
self.contactPicker.predicateForEnablingContact = enablePredicate;
[self presentViewController:self.contactPicker animated:YES completion:nil];
When it opens it currently looks like this:
However due to a bug in the SDK the search for people on this kind of view doesn't work, as you can not select from the search results. I'm going to file a bug for this but in the mean time firstly I want to hide the Search Bar. I found some old questions on removing the SearchBar but they related to the ABPeoplePickerNavigationController and are not relevant to CNContacts.
I also don't wish to use Groups and if I could remove that button and move the Cancel button over to the left that would be great, and would make the selection interface in my app much cleaner looking. This is how I would like it to look:
Could anyone tell me if this is possible and maybe point me in the right direction? I have the delegate method to receive the contacts array after selection, my problem is the way it looks in the app.
Thanks in advance!
Plasma
The real answer is the one you don't want to hear:
No, you can't modify the UI that Apple presents (at least in a meaningful way). You might be able to modify the tint color and other minor details, but you can't change whatever you like. If there's a bug in the search selector, you certainly can't fix that unfortunately. I'd suggest that just hiding the search bar isn't a great option, since for large contact lists, search is usually the primary way users will navigate to contacts.
Side note - a lot of Apple's framework view controllers are implemented
as a special sort of 'remote view controller'. The view controller
doesn't actually run as part of your app, but in the sandbox of the
parent application. This prevents any sort of trickery like traversing
and modifying the UI hierarchy of these presented controllers. I
wouldn't be surprised if that is the case here.
I would suggest that recreating the contact selection view isn't too hard, and gives you total flexibility in terms of customisation. I've done it myself, and there weren't any major hurdles to cross. Even if you're new to iOS, it would be a great learning exercise. For a good solution, you probably want to fetch all of the contacts on a background thread and display a loading spinner, since large contact databases could take a little while to fetch. (Even better, prefetch the contacts on the previous view, and only display a loading spinner if that fetch hasn't finished).
If you don't feel like doing that, I've seen a few contact selection frameworks around the place on GitHub. I'm not sure what the quality is like, but at the very least, these might provide a great starting point.
You can get an array of all contacts, and then display and work with it as you like:
- (NSMutableArray<CNContact *> *)allContacts {
NSMutableArray <CNContact *> *result = [NSMutableArray array];
NSError *error = nil;
NSArray *keysToFetch = #[CNContactEmailAddressesKey, CNContactPhoneNumbersKey, CNContactImageDataKey,
[CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName]];
CNContactStore *contactStore = [[CNContactStore alloc] init];
NSArray <CNContainer *> *allContainers = [contactStore containersMatchingPredicate:nil error:&error];
for (CNContainer *container in allContainers) {
NSPredicate *predicate = [CNContact predicateForContactsInContainerWithIdentifier:container.identifier];
NSArray *fetchedContacts = [contactStore unifiedContactsMatchingPredicate:predicate keysToFetch:keysToFetch error:&error];
[result addObjectsFromArray:fetchedContacts];
}
return result;}

objective-c How to cache images from facebook using SDWebImage?

Im new in iOS programming so i need some help.
I have a TableViewController with a property NSArray, where is stored Facebook User's friends info: uid, square_pic and name. I use to load their profile photos using FBProfilePictureView, by uid.
My problem: When user scroll the table pictures from cells take a lot of time to reload. I want to cache this photos using SDWebImage.
Thank's a lot.
I am not familiar to SDWebImage. Their readme mentions ability to cache, so you might want to figure it out on your own.
However, there is a really nice class called NSCache that you can implement in some of your managers or view controllers. Also, this post by Mattt Thompson covers it in great details.
It functions as a regular NSDictionary in sense that you can set object for keys and retrieve them. e.g:
//setting
[cache setObject:image forKey:#"yourImageURL"]
//accessing
UIImage *image = [cache objectForKey:#"yourImageURL"];
if (image == nil) {
// request it
}
But it also contains some awesome staff for setting values for objects which will serve as a hint which objects have to be removed in case of low memory.
Hope this helps to answer your question.
Cheers!

MagicalRecord: Create now, (possibly) save later

I've been using MagicalRecord quite a bit lately - and blimey it's been making my Core Data life much, much easier, so full credit to the author of what is a quality library!
Anyway, the scenario is I have navigation controller stack of 3 controllers, referred here from bottom to top as A-B-C:
View controller A is a form where I would like to fill in the details about the entity (i.e., its properties).
View controller B shows the result of a calculation from the entity (e.g., via an instance function).
For the sake of simplicity, view controller C is just a confirmation & is where I'd like to save the entity.
Is there a way in MagicalRecord I can call [MyEntity createEntity] in view controller A with its properties set, pass it through to C via B & only save it in C? This would also include the possibility of not saving it at all should the user decide to go back to A from B or C.
I fully appreciate I may well be getting the wrong end of the stick with Core Data & it may not be possible. As a workaround, already I know I can create a class method that does the same calculation given the relevant parameters & pass all the parameters through the stack from A to C.
Edit: Just to make it clear, I want to call [[NSManagedObjectContext defaultContext] save] in View Controller C.
yes sure.. just dont save the managedContext.
the VCs should run all on the main thread anyways.. so all can use
[NSManagedObjectContext defaultContext]
OR
pass the MOC between the three classes using a property on the controllers.
#property NSManagedObjectContext *context;
After some testing, I knew #Daij-Djan had the right idea in that I don't call [[NSManagedObjectContext defaultContext] save] if I don't want to save my entity. As a result I leave that call until View Controller C to do the saving.
However, my testing showed I need to do a little more to avoid saving any unwanted entities. I've noticed if I go to A from B via the back button, I want to discard the entity there. In my use-case, it doesn't matter if I just create another new entity going from A to B, and I never pass back through View Controller B if I have successfully saved the entity.
So basically I need to delete the unsaved entity if the back button is pressed on View Controller B. This answer helps me massively there, resulting in this code in View Controller B:
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self] == NSNotFound) {
// self.entity is the instance of my entity
[self.entity deleteEntity];
self.entity = nil;
}
[super viewWillDisappear:animated];
}

Can anyone help out an Objective C novice?

I feel i am totally out of my depth here, im very new to objective c but have been asked to design an iphone app as part of my uni course. I designed a sinple quiz before, but I was hoping to design a more advanced quiz game with 3 levels (each level will have a different quiz).
I dont know how to use the UIViews and I have tried a tutorial online to help me code a navigation controller. It allows gives me 3 options to go into subview 1, 2 or 3. All the subviews have the same screen though, with one label and a button.
I have 3 classes so far, RootViewController, BasicNavigationAppDelegate and SubViewOneController.
I really dont understand the code at all, im familiar with Java but this objective c is nothing like it. Could someone maybe take a minute to help out a person in distress and explain if i am doing this right by using the navigation controller to display my levels? When i check the xib interface files i dont see the button or label, or dont know where to add the quiz interface objects!! I really am confused by all this. Could anyone help?
You should search google for sample source code, and see how some of the views can be handled. There are many ways you can handle a view, whether its by a UINavigationController, UITabBarController, etc. If you are new to Objective-C, then your not really going to get an answer to this question that will instruct you on what exactly to do.
Interface Builder + View Controllers
Here's a good one for you: View Controllers Programming Guide
(Apple's) View Controller Reference Guide
Some Code Samples
Getting Started Sample Code
I recommend Head First iPhone Development: A Learner's Guide to Creating Objective-C Applications for the iPhone. Within a few chapters you'll know everything you need to build this app and you'll actually understand what you're doing.
(I don't know the authors or the publisher, I just think it's a great book for quickly getting up to speed.)
for a 3 level quiz, UINavigationController is definitely an option.
if you need to find out how to use a class, in xcode, type its name, then press -alt- and double click the class name, this will bring up a short description, with two icons, one will take you to the header file, and the other to the documentation.
to add elements to the nib/xib files, you will need to open the library window, where you will find labels, buttons etc. to use a button, you will need to define an action in your header file, and hook it up in IB, to be able to interact with UIElements in your code, you want to set up outlets in the header file, and hook them up in IB.
something you need to decide on, is how you are going to present the questions, and will also depend if the answer will be true/false, multiple choice, or text entry.
if you arent familiar with obj-c and xcode, it is probably worth picking up an ebook from someone like http://www.pragprog.com. they have an iPhone one up there by Bill Dudney which is quite good(i believe he now works for apple.)
for the standard slide out transition you could use this.
//you would probably want to call this something like level1NavBarItemWasPushed: instead
- (IBAction)lvl1pushNavBarItem:(id)sender {
//create instance of AnswersViewController class.
AnswersViewController *level1AnswersVC= [[Level1AnswersViewController alloc] init];
//pass it some kind of identifier so it can tell which quiz/question it is dealing with and pull in the answers, so that you can reuse the view
[level1AnswersVC setAnswersObject:<<insert object dictionary here>>];
//push the view controller onto the navigationController's view stack
[self.navigationController pushViewController:level1AnswersVC animated:TRUE];
//pushing it onto the view stack has given it +1 retain, so we can now release it without worrying about it disappearing prematurely.
[level1AnswersVC release];
}
for the page flip transition you could use this.
- (IBAction)lvl1pushNavBarItem:(id)sender {
//create instance of AnswersViewController class.
AnswersViewController *level1AnswersVC= [[Level1AnswersViewController alloc] init];
//pass it some kind of identifier so it can tell which quiz/question it is dealing with and pull in the answers, so that you can reuse the view
[level1AnswersVC setAnswersObject:<<insert object dictionary here>>];
//set the current viewController as the delegate, so that it can call back to us when its done
level1AnswersVC.delegate = self;
//set the modal transition style
level1AnswersVC.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
//show answers as modal view, which has been setup to use the page flip transition.
[self presentModalViewController:level1AnswersVC animated:YES];
//pushing it onto the view stack has given it +1 retain, so we can now release it without worrying about it disappearing prematurely.
[level1AnswersVC release];
}

Cocoa Touch UIViewController Properties and class design

I'm creating a custom ViewController. This VC needs to load some data that is known in the event that creates it and pushes it to the top of the NavigationController that it is going to be part of.
My question is, how should I pass data from the view that handles the custom ViewController's creation into that custom ViewController.
I've thought of four possible options, and I was hoping to get feedback on why each one is good or not for this functionality.
1) Expose public properties in the custom ViewController and set the UI elements in the view based on those properties in - (void) ViewDidLoad.
2) Expose the actual UI elements themselves and set their .text/.image/.whatever attributes as the ViewController is being created.
3) Create a custom constructor for the custom view and pass in the values I need to set up the UI elements
4) Create a custom model that both views have access to, set the data before the CustomView is created/pushed, and access that data in the ViewDidLoad event.
I'm still new to all of this, and I want to make sure that I understand the proper handling of these handoffs of data. It seems like something like this is probably a simple answer, but I'm still a little confused and its probably really important to do this right to avoid memory loss/leaks.
Also, in case anyone cares, I'm using Stanford's CS193p class on iTunes U and Mark/Lamarche's "Beginning iPhone Development" to teach myself cocoa for the iPhone. I'm working on an application with a NavigationController and a couple ViewControllers (Presence 1 if you're familiar with 193p).
Well, I believe there are advantages & disadvantages to each of those methods depending on your requirements...often it will require some combination of approaches. I believe the most common, for me anyway, is to do something like this where you give it enough to get started.
MyViewController *vc = [[MyViewController alloc] init]; // (or initWithNibName:bundle:)
// transfer vc values here
vc.value1 = aValue;
vc.value2 = anotherValue;
[self.navigationController pushViewController:vc animated:YES];
[vc release];
After your view controller is instantiated you have an opportunity to pass objects to it. Say MyViewController is a detail view then you'd give it the object it will be displaying the details for. Or, if it's a table view you can give it the NSArray it will need for display. Then in viewDidLoad or awakeFromNib or awakeFromCoder, or... you can fill out the view...so to speak.
#1 is fine, with or without #3 (these two are not mutually exclusive)
#4 is my preferred solution. For instance, if I had a UserViewController, I would probably also like to have a User object and create it this way:
User *user = [self.users objectAtIndex:someIndex];
UserViewController *uvc = [[[UserViewController alloc] initWithUser:user] autorelease];
#2 is not a good idea. Objects should not access the UI elements of other objects. Much trouble comes from this when you decide to change your UI around (and you will).