Is It Possible To Customise CNContactPickerViewController? - objective-c

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;}

Related

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!

Cocoa Message with no action required

I'm trying to figure out a way to give a user feedback when they have saved settings. similar to Microsoft's "File Saved" dialog Is there a class for this type of dialog? I do not want to require any action by the user. Just "Your setting have been saved" then disappears after a short delay. Maybe a better way to describe would be like a jQuery message box with a fade in fade out type thing
Is there a class for this type of dialog?
That isn't a "dialog", because you're not accepting input from the user. At best, it's an alert, and you could therefore use NSAlert (see also "Dialogs and Special Panels") however, what you are contemplating is contrary to the recommendations given in the HIG for "Alerts":
Avoid using an alert merely to give users information. Although it’s important to tell users about serious problems, such as the potential for data loss, users don’t appreciate being interrupted by alerts that are informative but not actionable. Instead of displaying an alert that merely informs, give users the information in another way, such as in an altered status indicator.
In other words, this probably wouldn't be considered a good user experience by the OS X-using population.
You can still do this, if you absolutely must, by creating a sheet or alert window and setting a timer to dismiss it.
A much better plan would be to have a label somewhere in your interface whose text could display this information, again using a timer to clear the notice after an appropriate duration.
Yet another option (possibly the best) would be to put this notice somewhere that the user only sees it upon request. The HIG mentions Mail.app's information area at the bottom of its sidebar, for example.
It is simple to fade a window in and out using the NSViewAnimation see also NSAnimation Class
An example I use something like this.
- (void)fadeWindowIn{
//--make sure the window starts from 0 alpha. Or you may get it jumping in view then try and fade in.
[theWindow setAlphaValue:0.0];
//-- set up the dictionary for the animation options
NSDictionary *dictIn = [NSDictionary dictionaryWithObjectsAndKeys:
theWindow,NSViewAnimationTargetKey,
NSViewAnimationFadeInEffect,NSViewAnimationEffectKey,nil];
NSViewAnimation * fadeWindowIntAnim = [[[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dictIn]] autorelease];
[fadeWindowIntAnim setAnimationCurve:NSAnimationLinear];
[fadeWindowIntAnim setDuration:2];
[fadeWindowIntAnim setFrameRate:20.0];
//--start the animation
[fadeWindowIntAnim startAnimation];
//--set the timer for the fade out animation
[NSTimer scheduledTimerWithTimeInterval:4.8 target:self selector:#selector(fadeWindowOut) userInfo:nil repeats:NO];
}
-(void)fadeWindowOut{
//-- fade the window.
NSDictionary *dictOut = [NSDictionary dictionaryWithObjectsAndKeys:
theWindow,NSViewAnimationTargetKey,
NSViewAnimationFadeOutEffect,NSViewAnimationEffectKey,nil];
NSViewAnimation * fadeOutAnim = [[[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dictOut]] autorelease];
[fadeOutAnim setAnimationCurve:NSAnimationLinear];
[fadeOutAnim setDuration:1.2];
[fadeOutAnim setFrameRate:20.0];
[fadeOutAnim startAnimation];
}
theWindow is the NSWindow or NSView you want to fade in and out. Read the references to understand the options.
You can create your own such popup (using NSTimer to dismiss as needed), but perhaps an easier way would be to use the existing third-party library at http://code.google.com/p/toast-notifications-ios/. This library emulates Android's "toast" functionality.
Note that this library is for iOS development (not OSX), but wasn't sure which platform you were planning to target. Regardless, it should be adaptable with a little work.
The other answers about timers and such cover that aspect of it pretty well. I just wanted to jump in and suggest you take a look at the Growl framework. This seems to be the preferred way to do this sort of passive notification until Apple builds it into the OS.
Among other things, it gives the user a lot of control over how the notifications look, where they live on the screen, how long they stay up, and which apps are even allowed to display them. And they do this without you having to write any code. The downside is that it's another thing for your users to have to install, which could be a deal breaker for your app.
They also recently moved into the App Store and started charging a nominal fee ($2 or $3, I think) which could be seen as a downside but I think of it as a more positive thing: users will have a much easier time installing it now.
Some apps that make use of Growl notifications include BBEdit, Transmission, Scrivener, Twitteriffic, etc. Which is to say that it's not a fly-by-night thing.
As a user, I hate it when apps try to roll their own notifications since I lose all of the control that I get with Growl.
Just a thought, anyway.

Variables in different navigation stack

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.

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];
}

What Cocoa/Core Foundation helper functions do you wish you knew about 2 years ago?

I just discovered the NSRect helper functions in NSGeometry.h (i.e. NSMidX, NSMaxX, etc...)
These would have made some repetitive coding much easier. I knew about NSMakeRect, NSMouseInRect, NSOffsetRect and many others but somehow missed the functions that aid in recalculating NSRect geometry.
I've found NSStringFrom*() helpful when logging structs like CGRect, CGPoint, etc.
You can find a comprehensive overview at Apple's Foundation Functions Reference (Wayback Machine link).
Helper function to draw three part images with left cap, fill and right cap. Ideal for custom buttons
void NSDrawThreePartImage(NSRect frame,
NSImage *startCap,
NSImage *centerFill,
NSImage *endCap,
BOOL vertical,
NSCompositingOperation op,
CGFloat alphaFraction,
BOOL flipped
);
Also look for NSDrawNinePartImage
This is one that I wish I had known about 6 months ago. I was creating our first iPhone application and I wanted to create a simple help file that was based on HTML using the UIWebView Controller.
However I could not figure out how to embed local images that I had stored in the Bundle and I did not want the user to have to have internet access to fetch the images from a server.
Little did I know I could do the following to get images from the Main Bundle
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSURL *bundleBaseURL = [NSURL fileURLWithPath: bundlePath];
[webView loadHTMLString:htmlContent baseURL: bundleBaseURL];
The Image in your HTML can then call local images directly.
<img src="yourImageFromTheMainBundle.jpg" />
I had no idea I could set the baseURL with the location of the Bundle.
Much of the stuff in NSPathUtilities.h. I did know about it two years ago, but when I first found it I wished I’d seen it two years earlier. :-)
At some point I wasted quite a bit of time because I didn’t know about NSCountedSet, and made a mess of my dictionary-based replacement. I know of several cases where people have done the same sort of thing because they didn’t know about NSSet at all. Another good “hidden” collection is CFBinaryHeap, which implements a priority queue, but doesn’t have an NS equivalent.
One I remember
+ (NSBezierPath *)bezierPathWithRoundedRect:(NSRect)rect xRadius:(CGFloat)xRadius yRadius:(CGFloat)yRadius
Granted figuring out how to draw a rounded rectangle manually is a pretty good exercise. There are others that I am just so used to now.
Creating a colour from a pattern image with
[UIColor colorWithPatternImage:[UIImage imageNamed:#"mypattern.png"]];
This is a shortcut instead of a library call that I missed, but it is in the spirit of the thread.
One shortcut that I use alot is using an inline format statement in NSLog calls.
NSLog(#"x=%#", [someobject className]);
instead of the more verbose
NSLog([NSString stringWithFormat:#"x=%#", [someobject classname]]);