I read in the documentation that the #property(nonatomic, copy) NSString *restorationIdentifier is capable of preserving the state of an UIImageView properties such as position, angle, etc. I tried adding the methods
-(BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder
{
return YES;
}
-(BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder
{
return YES;
}
to the view controller. I have set the restoration ID of the view controller as #"myFirstViewController in the IB.
I have added the following methods to the view controller as well.
-(void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
[coder encodeObject:_myImageView.image forKey:#"UnsavedImage"];
[super decodeRestorableStateWithCoder:coder];
}
-(void)decodeRestorableStateWithCoder:(NSCoder *)coder
{
_myImageView.image = [coder decodeObjectForKey:#"UnsavedImage"];
[super encodeRestorableStateWithCoder:coder];
}
Am i supposed to add the first two methods in the appDelegate or the view controller?
The UIImageView is not getting preserved. What is wrong here?
To make the state preservation and restoration work there are two steps that are always required:
The App delegate must opt-in
Each view controller or view to be
preserved/restored must have a restoration identifier assigned.
You should also implement encodeRestorableStateWithCoder: and decodeRestorableStateWithCoder: for views and view controllers that require state to be saved and restored.
Add the following methods to the view controller of your UIImageView.
-(void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
[coder encodeObject:UIImagePNGRepresentation(_imageView.image)
forKey:#"YourImageKey"];
[super decodeRestorableStateWithCoder:coder];
}
-(void)decodeRestorableStateWithCoder:(NSCoder *)coder
{
_imageView.image = [UIImage imageWithData:[coder decodeObjectForKey:#"YourImageKey"]];
[super encodeRestorableStateWithCoder:coder];
}
State preservation and restoration is an optional feature so you need to have the application delegate opt-in by implementing two methods:
- (BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder
{
return YES;
}
- (BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder
{
return YES;
}
Useful article about state preservation:
http://useyourloaf.com/blog/2013/05/21/state-preservation-and-restoration.html
Related
I want to know how I can get to know when a viewController appears, disappears from the main window from the UIApplication. I don't want to put code in each and every UIViewController, but observe lifecycle of each viewcontroller from the UIApplication.
Every view controller has a life cycle. So every view controller has separate life cycle method. So you have to put code each and every UIViewController. In app delegate we check the application state.
The state's are:
Active state
Inactive state
Background state
Not Running state
Suspended state
Delegate Method:
application:didFinishLaunchingWithOptions:
applicationWillResignActive:
applicationDidBecomeActive:
applicationDidEnterBackground:
applicationWillEnterForeground:
applicationWillTerminate:
View Controller Life cycle method:
-(void)viewDidLoad:(BOOL)animated{
[super viewDidLoad:animated];
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
-(void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}
I'm building a UILabel library that is mostly intended to be used programmatically but I want it to work with Interface Builder as well. When the user calls setText on my UILabel subclass, I watch it there and update the text as per the label's use, but it doesn't appear to be called with IB.
Should I just be watching awakeFromNib?
Question should read: Using KVO in UIView subclass
If so, -initWithCoder: is the preferred location while -awakeFromNib still works as mentioned by #KhanhNguyen.
The problem becomes How do I find my UILabel? and here is a possible answer using -viewWithTag:
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if(self = [super initWithCoder:aDecoder]) {
[self registerKVO:[self viewWithTag:1]
forKeyPath:NSStringFromSelector(#selector(text))];
}
return self;
}
Answering the general UIViewController question
Note - (instancetype)initWithCoder:(NSCoder *)aDecoder is not the correct place to install the KVO in a UIViewController since the view hasn't loaded and neither has the label.
Register KVO using a typical Referencing Outlet
- (void)viewDidLoad {
[super viewDidLoad];
[self registerKVO:self.label
forKeyPath:NSStringFromSelector(#selector(text))];
}
Regardless of your approach, responding to KVO looks like this:
#pragma mark - NSObject(NSKeyValueObserving)
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([keyPath isEqualToString:NSStringFromSelector(#selector(text))]) {
NSLog(#"%#",change[NSKeyValueChangeNewKey]);
}
}
...and setup + teardown like that:
- (void)registerKVO:(NSObject *)object forKeyPath:(NSString *)keypath {
[object addObserver:self
forKeyPath:keypath
options:NSKeyValueObservingOptionNew
context:nil];
}
- (void)dealloc {
[self removeObserver:self forKeyPath:NSStringFromSelector(#selector(text))];
#if !__has_feature(objc_arc)
[super dealloc];
#endif
}
I have a tabbed application with 2 storyboards. In the 2nd storyboard I have a restoration-ID.
I implemented the following in the AppDelegate:
-(BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder
{
return YES;
}
-(BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder
{
return YES;
}
and this in my SecondViewController:
-(void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
[coder encodeObject:self.myTextView.text forKey:#"unsavedText"];
[super encodeRestorableStateWithCoder:coder];
}
-(void)decodeRestorableStateWithCoder:(NSCoder *)coder
{
self.myTextView.text=[coder decodeObjectForKey:#"unsavedText"];
[super decodeRestorableStateWithCoder:coder];
}
But when i go to Home with iOS simulator , stop the running application and restart, the TextView doesn't restore text, my application starts in a first tab.
You may have missed assigning a restoration ID elsewhere in your view hierarchy.
Did you also change your AppDelegate from -application:didFinishLaunchingWithOptions: to application:willFinishLaunchingWithOptions:?
There are some useful state restoration tools available at the Downloads for Apple Developers page.
Search for restoration to find a debug profile which logs your app's state restoration to the console, as well as the restorationArchiveTool to dump the state restoration data in human-readable format.
I'm creating a chat app, and the user is first taken to the main menu, where they can select "chat" and then be directed to a FB Login feature, after the user logs in with FB, they are supposed to be redirected to a tab bar controller. However I cannot figure out how to set the programatic segue up so that it takes the user directly to that screen after a successful login.
I don't know if that's why Parse isn't collecting the User Data or not, but I get this warning when running the app: [10799:60b] Warning: A long-running Parse operation is being executed on the main thread. Break on warnParseOperationOnMainThread() to debug. My question is how do I create that segue to my Tab Bar Controller and am I missing something with Parse?
Here is my AppDelegate.m file:
#import "CCAppDelegate.h"
#import <Parse/Parse.h>
#implementation CCAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[Parse setApplicationId:#"XXXX"
clientKey:#"XXXX"];
[PFAnalytics trackAppOpenedWithLaunchOptions:launchOptions];
[PFFacebookUtils initializeFacebook];
[FBLoginView class];
[FBProfilePictureView class];
return YES;
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
BOOL wasHandled = [FBAppCall handleOpenURL:url sourceApplication:sourceApplication];
return wasHandled;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
#end
And here is my CCLoginViewController.m file:
#import "CCLoginViewController.h"
#interface CCLoginViewController ()
#property (strong, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
#end
#implementation CCLoginViewController
-(void)toggleHiddenState:(BOOL)shouldHide
{
self.lblUsername.hidden = shouldHide;
self.lblEmail.hidden = shouldHide;
self.profilePictureView.hidden = shouldHide;
}
-(void)loginViewShowingLoggedInUser:(FBLoginView *)loginView{
self.lblLoginStatus.text = #"You are logged in.";
[self toggleHiddenState:NO];
}
-(void)loginViewFetchedUserInfo:(FBLoginView *)loginView user:(id<FBGraphUser>)user{
NSLog(#"%#", user);
self.profilePictureView.profileID = user.objectID;
self.lblUsername.text = user.name;
self.lblEmail.text = [user objectForKey:#"email"];
}
-(void)loginViewShowingLoggedOutUser:(FBLoginView *)loginView{
self.lblLoginStatus.text = #"You are logged out";
[self toggleHiddenState:YES];
}
-(void)loginView:(FBLoginView *)loginView handleError:(NSError *)error{
NSLog(#"%#", [error localizedDescription]);
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
self.loginView.delegate = self;
[super viewDidLoad];
// Do any additional setup after loading the view.
[self toggleHiddenState:YES];
self.lblLoginStatus.text = #"";
self.lblLoginStatus.text = #"";
self.loginView.readPermissions = #[#"public_profile", #"email"];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
According to the docs on iOS, Facebook Users you should be calling FBAppCall slightly differently:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [FBAppCall handleOpenURL:url
sourceApplication:sourceApplication
withSession:[PFFacebookUtils session]];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[FBAppCall handleDidBecomeActiveWithSession:[PFFacebookUtils session]];
}
The key part you're missing is the withSession:[PFFacebookUtils session], which I'm guessing lets Parse know about what Facebook is doing.
I also don't see any Parse query in your code, so some other part of your code must be triggering that warning, somewhere you're doing a query without using a withBlock:, which lets it handle the results on a background thread without locking up the UI.
I'm trying to integrate a Facebook login into my app, and been doing so for the past two days without luck.
The login-button works: It logs me in and then changes text to "log out".
However, nothing else works. I can't get the name, I've tried placing random NSLog statements around in my code, but the only ones that prints are the ones in viewDidLoad. Nothing else.
Here's my code (I'm using a graphical implementation of the button btw)
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
NSLog(#"Print 1");
// Call FBAppCall's handleOpenURL:sourceApplication to handle Facebook app responses
BOOL wasHandled = [FBAppCall handleOpenURL:url sourceApplication:sourceApplication];
// You can add your app-specific url handling code here if needed
return wasHandled;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSLog(#"Print 2");
// Override point for customization after application launch.
[FBLoginView class];
self.loginView.readPermissions = #[#"basic_info", #"email", #"user_likes"];
NSLog(#"Initiating the FBLoginView");
return YES;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
NSLog(#"Print 3");
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
NSLog(#"Print 4");
}
return self;
}
- (void)loginViewFetchedUserInfo:(FBLoginView *)loginView user:(id<FBGraphUser>)user {
NSLog(#"Print 5");
}
- (void)loginViewShowingLoggedInUser:(FBLoginView *)loginView {
NSLog(#"Print 6");
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(#"Print 7");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
I'm very unsure as to where I'm gonna place the readPermissions, but I've tried nearly everything. I've also tried different stuff like
FBLoginView *loginView = [[FBLoginView alloc] init];
loginView.readPermissions = #[#"basic_info", #"email", #"user_likes"];
etc, but nothing works. Right now I have connected the loginView to my .h file via an IBOutlet, not sure if that's how it's done though. Facebooks guide online is really bad at this...
Can anyone help me? Why doesn't any of my NSLog statements print anything except the one in viewDidLoad?
You are forgetting to assign the delegate property of the login method. You need
self.loginView.delegate = self;
or else the delegate methods will never be called