Game Centre Leaderboard - Can't set GKLeaderboardViewController delegate to self (CCLayer) - ios

The following code won't compile because of the error that results from trying to set the GKLeaderboardViewController delegate to the calling instance.
The error message is:
Assigning to 'id<UINavigationControllerDelegate>' from incompatible type 'MainMenu *'
where MainMenu is of type CCLayer.
If the assignment statement (leaderboard.delegate = self) is commented out, the code will compile, the leaderboard will display, but the callback will not be called when the "done" button is pressed.
This is the code:
- (void) showLeaderBoard {
// Show GC leaderboard
GKLeaderboardViewController *leaderboard = [[GKLeaderboardViewController alloc] init];
if (leaderboard != nil) {
leaderboard.delegate = self;
leaderboard.category = #"ldrbrd_ref";
AppController *app = (AppController *)[[UIApplication sharedApplication] delegate];
[[app navController] presentViewController:leaderboard animated:YES completion:nil];
}
}
Incidentally, this is my header for the object declaration:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "CCGameButton.h"
#import <GameKit/GameKit.h>
#interface MainMenu : CCLayer <CCGameButtonDelegate, GKLeaderboardViewControllerDelegate> {
}
+ (CCScene *) scene;
#end
What am I doing wrong? Any help appreciated!

I believe what you are looking for is the leaderboardDelegate property rather than the delegate property. leaderboardDelegate requires a <GKLeaderboardViewControllerDelegate> while delegate requires a <UINavigationControllerDelegate>, hence the error message.
Check the apple docs for leaderboardDelegate
https://developer.apple.com/library/ios/documentation/GameKit/Reference/GKLeaderboardViewController_Ref/#//apple_ref/occ/instp/GKLeaderboardViewController/leaderboardDelegate

Related

How to fight "Collection was mutated while being enumerated" error?

I have native module for React Native, that opens Safari View Controller:
RCTSFSafariViewController.m:
#import "RCTSFSafariViewController.h"
#implementation RCTSFSafariViewController
#synthesize bridge = _bridge;
RCT_EXPORT_MODULE();
- (void)safariViewControllerDidFinish:(SFSafariViewController *)controller {
[self.bridge.eventDispatcher sendAppEventWithName:#"SFSafariViewControllerDismissed" body:nil];
}
RCT_EXPORT_METHOD(openURL:(NSString *)urlString params:(NSDictionary *)params) {
NSURL *url = [[NSURL alloc] initWithString:urlString];
SFSafariViewController *safariViewController = [[SFSafariViewController alloc] initWithURL:url];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:safariViewController];
[navigationController setNavigationBarHidden:YES animated:NO];
safariViewController.delegate = self;
if ([params objectForKey:#"tintColor"]) {
UIColor *tintColor = [RCTConvert UIColor:params[#"tintColor"]];
if([safariViewController respondsToSelector:#selector(setPreferredControlTintColor:)]) {
safariViewController.preferredControlTintColor = tintColor;
} else {
safariViewController.view.tintColor = tintColor;
}
}
dispatch_sync(dispatch_get_main_queue(), ^{
[rootViewController.rootViewController.presentedViewController presentViewController:navigationController animated:YES completion:^{
[self.bridge.eventDispatcher sendDeviceEventWithName:#"SFSafariViewControllerDidLoad" body:nil];
}];
});
}
RCT_EXPORT_METHOD(close) {
dispatch_async(dispatch_get_main_queue(), ^{
UIViewController *rootViewController = [[[UIApplication sharedApplication] delegate] window].rootViewController;
[rootViewController dismissViewControllerAnimated:YES completion:nil];
});
}
#end
RCTSFSafariViewController.h:
#import <React/RCTBridgeModule.h>
#import <React/RCTConvert.h>
#import <React/RCTEventDispatcher.h>
#import <UIKit/UIKit.h>
#import SafariServices;
#interface RCTSFSafariViewController : NSObject <RCTBridgeModule, SFSafariViewControllerDelegate>
#end
It works well in simulator and my iPhone, but a lot of users are facing such crash (according to Crashlytics):
Collection <__NSArrayM: 0x14e3bd20> was mutated while being enumerated.' was thrown while invoking openURL on target SFSafariViewController with params ( "https://example.com", { } )
The problem is that there is no arrays or enumerating constructions in this code. I have this idea that this could be caused by dispatch_async, because when I remove it, app stop crashing, but works incredebly slow after calling SVC.
What am I doing wrong?
I assume this is being called on a non-main queue. If it were on the main queue, then your dispatch_sync should deadlock. It is not safe to interact with most UIKit objects off the main queue. That likely includes SFSafariViewController (unless you have explicit documentation saying it is safe, then it isn't).
Calling dispatch_sync is generally dangerous (without care it can lead to deadlocks), and in this case unnecessary. You could definitely use dispatch_async here.
You should move this entire method into a dispatch_async call to the main queue.

How to call block defined as property in another class?

I am calling the block from second class which has been declared and maintained in first class.
In ViewController.h
#property (copy) void (^simpleBlock)(NSString*);
In View Controller.m
- (void)viewDidLoad {
[super viewDidLoad];
self.simpleBlock = ^(NSString *str)
{
NSLog(#"Hello My Name is: %#",str);
};
}
In SecondViewController.m
In ViewDidload
ViewController *VC = [[ViewController alloc]init];
VC.simpleBlock(#"Harjot");//bad execution error
Please suggest me some solutions because the code is giving me bad execution error.
How can i call the block in any another way?
It's the correct way of run the block. However if you try to run a block that is nil you'll have a crash - so you should always check that it's not nil before calling it:
ViewController *vc = [[ViewController alloc] init];
if (vc.simpleClock) {
vc.simpleBlock(#"Harjot");//this will not get called
}
The reason why in your case the block is nil is because you set it in viewDidLoad - however viewDidLoad is not called until its view is ready to go on screen. For testing purposes try to move the assignment from viewDidLoad to init and this should work:
- (instancetype)init
{
self [super init];
if (self) {
_simpleBlock = ^(NSString *str)
{
NSLog(#"Hello My Name is: %#",str);
};
}
return self;
}

Is it bad design to set self.delegate = self

I have a UIViewController subclass (say MyViewController).
MyViewController.h
#protocol TargetChangedDelegate
-(void) targetChanged;
#end
#interface MyViewController
#property (weak) id<TargetChangedDelegate> targetChangedDelegate;
-(void) doSomethingOnYourOwn;
#end
MyViewController.m
#implementation MyViewController <TargetChangedDelegate>
-(void) doSomethingOnYourOwn
{
// DO some stuff here
// IS THIS BAD ??
self.targetChangedDelegate = self;
}
-(IBAction) targetSelectionChanged
{
[self.targetChangedDelegate targetChanged];
}
-(void) targetChanged
{
// Do some stuff here
}
#end
Based on certain conditions a class that instantiates an instance of MyViewController may decide to set itself as the delegate or not.
Foo.m
#property(strong) MyViewController *myVC;
-(void) configureViews
{
self.myVC = [[MyViewController alloc] init];
[self.view addSubview:self.myVC];
if (someCondition)
{
self.myVC.targetChangedDelegate = self;
}
else
{
[self.myVC doSomethingOnYourOwn]
//MyViewController sets itself as the targetChangedDelegate
}
}
With reference to the code snippet above, I have the following question:
Is it a violation of MVC/delegation design pattern (or just a bad design) to say:
self.delegate = self;
There's absolutely no problem with setting the delegate to self. In fact it is a good way to provide default delegate functionality if a delegate is not set by somebody else.
Obviously, the delegate property has to be declared weak otherwise you get a reference cycle.
To expand a bit, having read the wrong answer and wrong comments above, if you allow an object to be its own delegate, your code is cleaner because you do not have to surround absolutely every single delegate call with
if ([self delegate] != nil)
{
[[self delegate] someMethod];
}
else
{
[self someMethod];
}
Its not proper way to assign self.delegate = self.
for your functionality, you can do this:
-(void) doSomethingOnYourOwn
{
// DO some stuff here
self.targetChangedDelegate = nil;
}
and when using delegate:
if(self.targetChangedDelegate != nil && [self.targetChangedDelegate respondsToSelector:#selector(targetChanged)]
{
[self.targetChangedDelegate targetChanged];
}
else
{
[self targetChanged];
}
It is bad design to set self.delegate = self; it should be another object. Delegation via protocols are an alternative design to subclassing and you can read more about delegation here:
https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html
And here is more on protocols:
https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/Protocol.html

iOS 9 custom transition - animationControllerForDismissedController not called

I am a newbee in iOS development and recently run into this problem with customized transition in iOS 9.
I have an object conforms to UIViewControllerTransitioningDelegate protocol and implements animationControllerForDismissedController, something like:
#implementation MyCustomizedTransitioningDelegate
#pragma mark - UIViewControllerTransitioningDelegate
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
MyCustomizedTransitionAnimator *animator = [[MyCustomizedTransitionAnimator alloc] init];
animator.presenting = NO;
return animator;
}
#end
And the process that triggers the modal transition is something like:
#implementation MyViewController
#pragma mark - Initializers
+ (MyCustomizedTransitioningDelegate *)modalTransitioningDelegateSingletonInstance;
{
static MyCustomizedTransitioningDelegate *delegateInst = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
delegateInst = [[MyCustomizedTransitioningDelegate alloc] init];
});
return delegateInst;
}
#pragma mark - UIViewController
- (void)dismissViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion;
{
[self prepareForDismissViewControllerAnimated:animated completion:&completion];
[super dismissViewControllerAnimated:animated completion:completion];
}
- (void)prepareForDismissViewControllerAnimated:(BOOL)animated completion:(dispatch_block_t *)completion;
{
self.presentedViewController.modalPresentationStyle = UIModalPresentationCustom;
self.presentedViewController.transitioningDelegate = [[self class] modalTransitioningDelegateSingletonInstance];
}
#end
Since animationControllerForDismissedController method is not called, the MyCustomizedTransitionAnimator is not created, which leads to its animateTransition not called either, which causes unexpected problem in my app. (Sorry for my poor English...)
I am also attaching the screenshot of stack trace for both iOS8 & iOS9.
In iOS 8, animationControllerForDismissedController is called after the stack trace below.
But in iOS9, transitionDidFinish is called somehow in advance, which I guess probably prevent animationControllerForDismissedController being called?
I was wondering if this is an iOS 9 bug or not. Any explanation or work around solution will be greatly appreciated!
I faced the same issue.
I hope this will help someone.
What fixed it for me is to make the object which applies UIViewControllerTransitioningDelegate protocol as variable instance to keep strong relationship with it.
I think because it gets dismissed after the view is presented first time.
I had the same issue.
Turned out I needed to set the delegate on the navigationController of the UIViewController that contains the trigger button.
Having this old code that didn't work:
UIViewController *dvc = [self sourceViewController];
TransitionDelegate *transitionDelegate = [TransitionDelegate new];
dvc.modalPresentationStyle = UIModalPresentationCustom;
dvc.transitioningDelegate = transitionDelegate;
[dvc dismissViewControllerAnimated:YES completion:nil];
I changed the first line to:
UIViewController *dvc = [self sourceViewController].navigationController;
and it worked.
Hope this helps.
You need to say something like:
MyDestinationViewController *viewController = [[MyDestinationViewController alloc] init];
MyCustomizedTransitioningDelegate *transitioningDelegate = [[MyCustomizedTransitioningDelegate alloc]init];
viewController.transitioningDelegate = transitioningDelegate;
viewController.modalPresentationStyle = UIModalPresentationCustom;
[self presentViewController: viewController animated:YES completion:nil];
Or if you're using segues, in prepareForSegue say something like:
MyDestinationViewController *toVC = segue.destinationViewController;
MyCustomizedTransitioningDelegate *transitioningDelegate = [[MyCustomizedTransitioningDelegate alloc]init];
toVC.transitioningDelegate = transitioningDelegate;

ShareKit lose focus when actionsheet is closed

I´m trying to integrate ShareKit in my ios game.
Everything is working fine and the actionsheet is shown and I can interact with it but I´m not able to return the focus to my app when the sharekit action has finished (by closing the actionsheet or finishing any action).
I have tried in several ways but any has worked for me. What´s happening?
I´m not an expert programmer so I expect I´m missing something.
I´m
This is my .h
#import <UIKit/UIKit.h>
#import "SHK.h"
#import "SHKConfiguration.h"
#interface SocialWrapper: UIViewController{
}
- (id) init;
- (void) open;
- (void) dealloc;
#end
and .m
#import "SocializeWrapper.h"
#implementation SocialWrapper
- (id) init {
self=[super init];
DefaultSHKConfigurator *configurator = [[DefaultSHKConfigurator alloc] init];
[SHKConfiguration sharedInstanceWithConfigurator:configurator];
[SHK flushOfflineQueue];
return self;
}
- (void) open
{
NSString *someText = #"Hello Earth!";
SHKItem *item = [SHKItem text:someText];
// Get the ShareKit action sheet
SHKActionSheet *actionSheet = [SHKActionSheet actionSheetForItem:item];
UIWindow *window = [UIApplication sharedApplication].keyWindow;
[window addSubview:self.view];
[SHK setRootViewController:self];
[actionSheet showInView:self.view];
}
- (void) dealloc {
NSLog(#"SHK dealloc");
[self release];
[super dealloc];
}
#end
I´m calling it by using this wrapper
#import "SocializeWrapper.h"
SocialWrapper *socialize;
void SHKinit(void) {
NSLog(#"SHK Init");
socialize = [[SocialWrapper alloc] init];
}
void SHKopenWeb(void){
NSLog(#"SHK Open actionsheet");
[socialize open];
}
I´m working with ios 5, xcode 4.3.2 and the last sharekit version from the git.
I think I have to dissmiss my SocialWrapper once the actionsheet is closed but I don´t know how to capture that event, or even if this is correct. I´m stucked.
any help will be greatly appreciated.
UPDATE
As comment adviced, now the controller is on a category, using the actionsheet delegate, the focus can be regained when clicking the cancel´s actionsheet button. The problem still persists when an action is finished or cancelled. Don´t know how to capture that event.
This is my category code:
#import "SocialWrapper.h"
#implementation UIViewController (SocialController)
-(void) loadconfig
{
DefaultSHKConfigurator *configurator = [[DefaultSHKConfigurator alloc] init];
[SHKConfiguration sharedInstanceWithConfigurator:configurator];
[SHK flushOfflineQueue];
}
- (void) open
{
NSLog(#"Opening social button");
NSString *someText = #"Monkey Armada rules!";
SHKItem *item = [SHKItem text:someText];
// Get the ShareKit action sheet
SHKActionSheet *actionSheet = [SHKActionSheet actionSheetForItem:item];
UIWindow *window = [UIApplication sharedApplication].keyWindow;
[window addSubview:self.view];
[actionSheet setDelegate:self];
[SHK setRootViewController:self];
[actionSheet showInView:self.view];
}
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
NSLog(#"SHK actionsheet dissmiss with button %d", buttonIndex);
if(buttonIndex == 4)
{
NSLog(#"SHK close actionsheet");
[self dismissViewControllerAnimated:YES completion:nil];
[self.view removeFromSuperview];
}
}
#end
Well since SHKActionSheet is a subclass of UIActionSheet you can set the delegate of that class to self to know when the dismissal happens.
Also, [self release]; in dealloc is complete misunderstanding of what release does, if you're in dealloc then releasing self won't do anything !
Learn the memory management rules.
I should also warn you that [window addSubview:self.view] is deprecated, you should not do that at all. In fact, I don't see a reason to wrap share kit stuff each view controller should be able to write that code easily. At worse you could put that code in a category on UIViewController if you don't want to rewrite the code every time.

Resources