Game Kit with iOS6 - uiviewcontroller

I recently tried to authenticate the local user on iOS 6 with the new iOS 6 method, and it returns in the authenticate handler-
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error)
How do I display this view controller? (FYI- GC automatically dismisses the window when completed authenticating the local player)
I'm using storyboard in the project.
Thanks!

Once you set the handler, it will be called at different situations. In those calls, the viewController parameter can be nil or it can actually be an instance GKHostedAuthenticateViewController.
You need to check if the viewController is not nil, and in that case, you can display it as you would do with any other view controller.
Here you have some sample code from the Game Center Programming guide
- (void) authenticateLocalPlayer
{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error){
if (viewController != nil)
{
[self showAuthenticationDialogWhenReasonable: viewController
}
else if (localPlayer.isAuthenticated)
{
[self authenticatedPlayer: localPlayer];
}
else
{
[self disableGameCenter];
}
}];
}
Check out the GKLocalPlayer Class Reference and the Game Center Programming Guide

Related

Why is dismissing uinavigationcontroller not releasing view controller memory?

Implementing UIDocumentBrowserViewController in an existing app. This vc, in iOS 11 is the root view controller, and tapping a file creates my doc, instantiates my view controller, and presents it inside a UINavigationController. It all works, in that the files display, the proper document opens, the vc displays and works as expected. I had to add a left button to the nav bar to provide a way to close the doc/vc.
When I tap the "Done" button, the view controller closes and returns to the document browser. All of that is good.
The problem is that the view controller's memory isn't releasing (and the domino effect of document memory, etc then not releasing). In the iOS 10 side of things, with a UICollectionViewController embedded in a UINavigationController as the initial vc, but the doc and the display vc identical code for iOS 10 & 11, all memory releases. I've studied How to correctly dismiss a UINavigationController that's presented as a modal? and related posts, tried dozens of alternatives, and am just not seeing what I'm missing. Instruments isn't showing any memory leaks, though I see document objects in memory after dismissing the view controller. The log shows that the vc's viewWillDisappear is being called at the proper time.
I appreciate any insights into why the vc memory isn't being released (deinit() not being called).
Thank you.
#available(iOS 11.0, *)
class DocumentBrowserViewController: UIDocumentBrowserViewController, UIDocumentBrowserViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
allowsDocumentCreation = true
allowsPickingMultipleItems = false
}
// MARK: Document Presentation
func returnToDocumentBrowser(sender: UIBarButtonItem) {
print("returnToDocumentBrowser")
if let controller = self.presentedViewController as? UINavigationController {
controller.dismiss(animated: true, completion: nil)
}
}
func presentDocument(at documentURL: URL) {
print("present document")
let doc = MyDocument(fileURL: documentURL)
doc.open(completionHandler: { (success) in
if (success) {
print("open succeeded")
DispatchQueue.main.async {
let myController = self.storyboard!.instantiateViewController(withIdentifier: "my controller") as! MyViewController
myController.navigationItem.setLeftBarButton(UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.done, target: self, action: #selector(self.returnToDocumentBrowser(sender:))), animated: false)
myController.doc = doc
let navigationController = UINavigationController(rootViewController: myController)
self.present(navigationController, animated: true, completion: nil)
}
} else {
print("open failed")
}
})
}
}
The navigation stack seems at the heart of this. In the iOS 10 version, a collection view controller is the root view controller and is embedded in a navigation controller via storyboard.
In the iOS 11 version, the document browser view controller is the root view controller and cannot be embedded in a navigation controller. When I presented in the original code, my document view controller became the navigation stack's root view controller, and that cannot be popped, for example.
So I figured that maybe the navigation controller needed a different root view controller to mimic the prior behavior as much as possible and require the fewest code changes.
So I changed
let navigationController = UINavigationController(rootViewController: myController)
self.present(navigationController, animated: true, completion: nil)
to
let dummyVC = UIViewController()
let navigationController = UINavigationController(rootViewController: dummyVC)
from.present(navigationController, animated: true, completion: nil)
dummyVC.navigationController?.pushViewController(myController, animated: true)
This nav controller setup added a back button, meaning I didn't have to add a back button, and that handled popping the view controller automatically. As a result, I only needed to dismiss the nav controller, since it wasn't the app's root vc.
Finally, for the comment that the delegate should be declared weak, that's Apple's doing, and I just use it as provided. I'm not sure what affect that's having on things.

Whats the best way to setup & populate my nstableview with json data?

I'm sorry if this has already been answered. I'm a bit of a noob. From all the examples I've found I can't seem to work this out.
I'm building a mac osx application using Xcode 5. My interface that I've created in Interface builder has three tableviews. My data to populate these views is json format and it is being received successfully using NSURLConnection.
What is the best way to popular the tableviews? A lot of the tutorials I've followed use an arraycontroller that is dragged and dropped using IB. From what I understand I don't need an array controller and need to hand code controllers for each of tableviews. Is that correct? If so, please explain how I go about this and setup the bindings.
Also, when clicking on a column in tableview 1 I'm planning on making it adjust the data in tableview 2 & 3 so if that impacts anything please let me know.
Thanks in advance!
Update 6 Jan
Here is my code currently..
ContactsTableViewController.h
#import <Foundation/Foundation.h>
#interface ContactsTableViewController : NSObject <NSTableViewDataSource, NSTableViewDelegate> {
IBOutlet NSTableView *contactsTableView;
}
-(id) init;
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView;
-(NSView *) tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row;
#end
ContactsTableViewController.m
#import "ContactsTableViewController.h"
#implementation ContactsTableViewController
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
NSLog(#"numberOfRowsInTableView called");
return 1;
}
- (id) init {
NSLog(#"init called");
[contactsTableView setDelegate:self];
[contactsTableView setDataSource:self];
[contactsTableView reloadData];
return self;
}
- (void) initme {
NSLog(#"customfunc");
}
-(NSView *) tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSLog(#"viewForTableColumn called");
NSTextField *result = [tableView makeViewWithIdentifier:#"name" owner:self];// result is now guaranteed to be valid,
result.stringValue = #"My Name"; //[self.nameArray objectAtIndex:row];
return result;
}
#end
In my appDelegate:
- (void) awakeFromNib {
NSLog(#"AWAKE");
ContactsTableViewController *contactsController = [[ContactsTableViewController alloc] init];
}
Currently nothing is showing in the tableview and numberOfRowsInTableView & viewForTableColumn aren't getting called.
Look at the UITableViewDataSource, you need to implement the protocol in one of your classes, probable your view controller, implement the UITableViewDelegate to get row selection events in the same class.

Change view when didReceiveRemoteNotification

I am trying to change from the App delegate method to the Master View when I receive a Remote Notification, in order to perform a segue in the Master View to another view, but I am getting an NSInvalidArgumentException
Code in App Delegate when didReceiveRemoteNotification:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
EmergencyMasterViewController* maincontroller = (EmergencyMasterViewController*)self.window.rootViewController;
[maincontroller alert];
}
Code in MasterView:
-(void)alert
{
[self performSegueWithIdentifier: #"Warning" sender: self];
}
And the error I am getting: [UINavigationController alert]: unrecognized selector
It is because your window rootViewController is actually a UINavigationController instead of your EmergencyMasterViewController. You need to check how you assign the window root view controller in your app delegate didFinishLaunchingWithOptions or something similar.
Try to get the view controller embedded in the navigation controller, for example:
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
NSArray *viewControllers = navigationController.viewControllers
EmergencyMasterViewController *maincontroller = [viewControllers objectAtIndex:0];
It might be safer for the UINavigationController to pop to root view controller first before you try to get the EmergencyMasterViewController, in case the user is already navigating his way through the navigation stack:
[navigationController popToRootViewControllerAnimated:NO];

Segue not transitioning to next view?

I have been trying to figure this out for well over 3 hours now. After I successfully authorize my login with facebook, when the view tries to transition to the next view, it crashes with sigbart error: [4923:c07] * Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key SegueToScene1.'
This is my segue to scene:
- (IBAction)loginButtonTouchHandler:(id)sender {
// Set permissions required from the facebook user account
NSArray *permissionsArray = #[ #"user_about_me", #"user_relationships", #"user_birthday", #"user_location"];
// Login PFUser using facebook
[PFFacebookUtils logInWithPermissions:permissionsArray block:^(PFUser *user, NSError *error) {
[_activityIndicator stopAnimating]; // Hide loading indicator
if (user.isNew) {
NSLog(#"User with facebook signed up and logged in!");
[self performSegueWithIdentifier: #"SegueToScene1"
sender: self];
} else {
NSLog(#"User with facebook logged in!");
[self performSegueWithIdentifier: #"SegueToScene1"
sender: self];
}}];
[_activityIndicator startAnimating]; // Show loading indicator until login is finished
}
My question is, why does xcode keep refusing the segue?
Edit: I deleted my segue and put a new one "SegueMain", then changed the code to reflect that, but strangely, it still returns the same error with SegueToScene1. How strange is this? There is no trace of that title left...anywhere. Yet, it remains...
I imagine you have something connected in interface builder to perform the SegueToScene1 segue. To find it you could try searching the plain text version of your storyboard or you should probably have an idea of where it would be.

What is the proper way to unload views in iOS 6 in a memory warning (Apple doc flaw)?

In iOS 6, viewWillUnload and viewDidUnload are deprecated and UIViewControllers no longer unload views that are not visible on screen during a memory warning. The View Controller Programming Guide has an example of how to manually restore this behavior.
Here is the code sample:
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Add code to clean up any of your own resources that are no longer necessary.
if ([self.view window] == nil)
{
// Add code to preserve data stored in the views that might be
// needed later.
// Add code to clean up other strong references to the view in
// the view hierarchy.
self.view = nil;
}
}
Below the code sample is the following note:
The next time the view property is accessed, the view is reloaded
exactly as it was the first time.
There is an obvious flaw here. If a view controller that has not loaded its view receives a memory warning it will load its view in the line if ([self.view window] == nil) and then proceed to clean up and release it again. At best, this is inefficient. At worst, it makes the memory conditions worse if a complex view hierarchy and supporting data are loaded. I verified this behavior in the iOS simulator.
I can certainly code around this but seems odd for Apple docs to have such an error. Am I missing something?
The correct check in a view controller for the view being loaded and on screen is:
if ([self isViewLoaded] && [self.view window] == nil)
My full solution in iOS 6 to have a view controller unload views and cleanup similar to iOS 5 is the following:
// will not be called in iOS 6, see iOS docs
- (void)viewWillUnload
{
[super viewWillUnload];
[self my_viewWillUnload];
}
// will not be called in iOS 6, see iOS docs
- (void)viewDidUnload
{
[super viewDidUnload];
[self my_viewDidUnload];
}
// in iOS 6, view is no longer unloaded so do it manually
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if ([self isViewLoaded] && [self.view window] == nil) {
[self my_viewWillUnload];
self.view = nil;
[self my_viewDidUnload];
}
}
- (void)my_viewWillUnload
{
// prepare to unload view
}
- (void)my_viewDidUnload
{
// the view is unloaded, clean up as normal
}