I have an EAGLView-based class that runs the following code when a menu selection is made in OpenGL:
-(void) startPicker
{
self.gameState = kStatePicker;
GKPeerPickerController *picker = [[GKPeerPickerController alloc] init];
picker.delegate = self;
picker.connectionTypesMask = GKPeerPickerConnectionTypeNearby;
[picker show];
}
For some unknown reason, the picker is a blank rounded rect when it appears, with no visible interface or directions.
If I launch it at the end of my init function, it works. Afterward, it launches fine from the menu.
I have tried placing the code in startPicker inside a main queue dispatch, but that doesn't seem to help. I've tried running the picker with and without ARC, but that makes no difference. This code is more or less directly taken from the GKTank example that Apple provided a while ago, to introduce GameKit's bluetooth framework.
Can anyone tell me why this might be happening, and what a possible solution is?
Just following up. I discovered in the view controller that contains this view that there was a call to [UIView setAnimations:NO]; This prohibits GKPeerPickerController from appearing properly.
Related
In iOS15, there is a new API to SFSafariViewController.Configuration that suggest one can add a custom button on the SafariViewController UI. This blogpost explains this in greater detail, under "Running custom extension".
I've been trying to implement this in a sample app without success. See the code snippet below:
- (void)openSfariViewController {
SFSafariViewControllerConfiguration *config = [[SFSafariViewControllerConfiguration alloc] init];
// Also tried the following extensionIdentifiers without success
// com.atomicbird.DemoNotes
// com.apple.share-services
SFSafariViewControllerActivityButton *button = [[SFSafariViewControllerActivityButton alloc] initWithTemplateImage:[UIImage systemImageNamed:#"heart.fill"] extensionIdentifier:#"com.atomicbird.DemoNotes.DemoNotes"];
config.activityButton = button;
SFSafariViewController *safariVC = [[SFSafariViewController alloc]initWithURL:[NSURL URLWithString:#"http://developer.apple.com"] configuration:config];
// safariVC.delegate = self;
[self presentViewController:safariVC animated:NO completion:nil];}
However I try to configure the new activityButton, it doesn't seem to have an effect on how SFSafariViewController appears when it is presented. It looks exactly like it does if I don't configure the activityButton. Here are some things I think I might got wrong:
I don't know what is the extensionIdentifier, perhaps I used a wrong value?
Perhaps I got the whole thing wrong, and an ActivityButton is not what I think it is?
Maybe the Share extension is not the configured properly?
I haven't been able to find any information on the web on how an activityButton is even supposed to look like. Thanks for reading this far, let me know if you have any pointers for me.
We are using cocos2d-js to develop an iOS App which can launch different games. So I add an button in the native app viewcontroller and start the game by clicking the button, just like this:
-(void)didClickGame2Btn:(id)sender
{
//加载游戏
cocos2d::Application *app = cocos2d::Application::getInstance();
// Initialize the GLView attributes
app->initGLContextAttrs();
cocos2d::GLViewImpl::convertAttrs();
// Use RootViewController to manage CCEAGLView
RootViewController *rootViewController = [[RootViewController alloc] init];
rootViewController.wantsFullScreenLayout = YES;
[self.navigationController presentViewController:rootViewController animated:YES completion:^{
// IMPORTANT: Setting the GLView should be done after creating the RootViewController
cocos2d::GLView *glview = cocos2d::GLViewImpl::createWithEAGLView((__bridge void *)rootViewController.view);
cocos2d::Director::getInstance()->setOpenGLView(glview);
NSString *documentDir = [SEGetDirectories dirDoc];
NSString *wPath = [NSString stringWithFormat:#"%#/GameData/Game2",documentDir];
NSLog(#"document------:%#",documentDir);
std::vector<std::string> searchPathList;
searchPathList.push_back([wPath UTF8String]);
cocos2d::FileUtils::getInstance()->setSearchPaths(searchPathList);
//run the cocos2d-x game scene
app->run();
}];
[[UIApplication sharedApplication] setStatusBarHidden:true];
}
the rootViewController contains the game view. And then we add an button in the game, which is used to exit the game. The click event code of the exit game button likes:
//exit the game and close the view controller
gameEndCallBack:function(sender){
cc.log("director end............");
cc.director.end();
var ojb = jsb.reflection.callStaticMethod("ViewControllerUtils", "dismissCurrentVC");
}
We use the reflection to dismiss the rootViewController:
+(void)dismissCurrentVC
{
UIViewController *currentVC = [ViewControllerUtils getCurrentVC]; //这里获取最顶层的viewcontroller
[currentVC dismissViewControllerAnimated:YES completion:^{
NSLog(#"xxx");
}];
}
Everything is ok when the first time to enter the game, but after dismissing the rootViewController, we try to enter the game again, it crash.
The crash line is in the ScriptingCore::runScript metod and executing the code:
evaluatedOK = JS_ExecuteScript(cx, global, *script, &rval);
And the crash info is "exc_bad_access".
It is much the same problem as this topic, but the approaches in it did not solve the problem.
http://discuss.cocos2d-x.org/t/how-to-destroy-a-cocos-game-on-ios-completely/23805
This problem has been confusing me serveral days, I have no solution for this. Can anyone give me some help?
You can make the app to support multiple games with in the app.
All you have done is required but in addition to that please follow the below instructions.
First of all, cocos provide a singleton instance of cocos2d::Application that can not be restarted again especially in iOS. So the approach of ending the Director cc.director.end(); won't help you.
You should start the application only once by using the function call cocos2d::Application::getInstance()->run(); and next time if you want to start the game layer, you should not call this method again.
Instead, just pause cocos2d::Director::getInstance()->pause(); and resume cocos2d::Director::getInstance()->resume(); the director when you want to stop the game.
In this approach, if you dismiss/dealloc the view-controller instance then you should create the glview cocos2d::GLView instance again without calling the run method.
One more problem is, take care of the delay in loading the new scene. GLView will display the previous game scene for a while. Do a work around that will show blank screen while the new scene is ready.
Hope this will help you.
I have a piece of code that runs fine on iOS7, but has issues in iOS8.
It is a popup, that is initialized from a nib and immediately after, it's show method is called.
The problem is, that the initFromNib in iOS8 returns immediately, before calling viewDidLoad.
Consequently all IBOutlets, that the code relies on in show are nil.
The same code is used at multiple occasions, it works with a datepicker embedded in the popup, but it seems that the tableview, that is embedded in the popup in this special case is not initializing immediately on initFromNib.
To repeat the question: Is this a new behavior in iOS8 and
How should I handle this case in objC:
Init a popup from nib and show it immediately after initialization has been finished.
Currently, I would implement my show method as a delegate that is called at the end of viewDidLoad, but this would integrate too much external workflow into the popup for my opinion. But then, I am not really experienced in iOS development ...
Thanks for helping out or pointing me to matching resources.
UPDATE: This is the code calling the view
- (void)onButtonTapped:(id)sender {
if(shouldResignFirstResponder){
[[[UIApplication sharedApplication] keyWindow] endEditing:YES];
}
isFirstResponder = YES;
if (self.beginEditingBlock!=nil){
self.beginEditingBlock([self fieldId]);
}
JXCheckListPopoverController *popover = [[JXCheckListPopoverController alloc]
initWithNibName:#"JXCheckListPopoverController" bundle:nil];
if([self getEditableFieldType] == EditableField_multipleStrings) {
valuesBeforeEditing = [self.fieldValues copy];
[popover setupPopoverWithParams:params delegate:self
fieldValues:self.fieldValues readOnly:readOnly];
} else {
NSMutableDictionary *selectedValue = [[NSMutableDictionary alloc]
initWithObjectsAndKeys:fieldValue, #"selectedValue", nil];
self.fieldValueBeforeEditing = fieldValue ? fieldValue : #"";
[popover setupPopoverWithParams:params
delegate:self fieldValues:selectedValue readOnly:readOnly];
}
[popover show:self.bounds parent:self];
[[NSNotificationCenter defaultCenter] postNotificationName:#"fieldSelected" object:self];
self.startDate = [NSDate date];
}
viewDidLoad is called after this method has finished. I understand, that this is somewhat uncool, but I inherited this code as a bad heritage and would like to understand how to rework the workflow. As there may be hundreds of popover views present during editing (present but not visible), I cannot create them all upfront - at least not as far as as I understand the code.
This is the show method:
- (void)show:(CGRect)rect parent:(UIView *)parent {
if ([self.popoverController isPopoverVisible]){
[self.popoverController dismissPopoverAnimated:YES];
}
UIPopoverController *popover = [[UIPopoverController alloc]initWithContentViewController:self];
popover.delegate = self;
const CGFloat height = contentTableView.rowHeight* possibleValues.count;
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenHeight = screenRect.size.height;
//todo: replace hardcoded sizes here
[popover setPopoverContentSize:CGSizeMake(350.0, MIN(height,screenHeight))];
self.popoverController = popover;
if (parent.window!=nil){
[self.popoverController presentPopoverFromRect:rect inView:parent permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
[[NSNotificationCenter defaultCenter] postNotificationName:NOTIF_popupShow object:nil];
}
The problem is that you were always doing this wrong. iOS 8 has merely exposed the problem. Keep in mind what viewDidLoad means. It means that this view has loaded and that any outlets are now connected. Nothing more. It exists, but that is all. In particular, it is not in the interface yet. So what you were doing in viewDidLoad, you were doing too soon. Use viewDidAppear: or viewDidLayoutSubviews, for example, to know when the view is actually in the interface and we are ready to do something else.
Behavior for popovers has changed in iOS8. I'm not sure about regular view lifecycle thought. One would think that viewDidLoad will be called in a sync manner when you instantiate view from a XIB file, but judging by your comments it's not the case.
If I were on your place - I would try to create a sample small project where you can pin-point the problem and solution exactly. I know that re-factoring large body of code might not be an easy solution, but it might be necessary. In my projects I'm trying to wrap application logic for presenting alerts and popovers in a way so all differences between OS versions and devices are hidden from the application code and can adjusted easily.
I have found a solution to the problem.
I have changed the code to set the height of the tableview contained in the popup in the viewWillAppear method:
const CGFloat height = contentTableView.rowHeight* possibleValues.count;
CGFloat screenHeight = screenRect.size.height;
[popover setPopoverContentSize:CGSizeMake(350.0, MIN(height,screenHeight))];
This did not hurt the logic and it solved the problem.
The answer to the question is possibly that some change was there in nib loading, but I could not find a reference anywhere.
So I accept my answer as correct, as it solves my problem, but I feel kind of bad about it, because it does not explain the real issue. I have given the other persons answering a "+1" on their answers.
This is used to work fine for my pre-ARC code, but since refactoring all the project to be ARC compatible, I start getting this crash:
[CustomViewController respondsToSelector:]: message sent to deallocated instance
My project is an iPad app with a split view, but contrary to apple documentation, previous developer has put another view controller to be visible on app launch before split view. So I know this is not the right way to do, but as I said it used to work before ARC integration so I need to get a workaround with this.
The root view controller which contain a menu of items, each item display a detail form to be filled, then a click on next button move to the next detail screen, etc.
The issue starts when I click on home button put on root view to get back to the home view, here is the relevant code that move the user to the home screen:
//this method is in the appdelegate, and it gets called when clikc on home button located on the root view
- (void) showHome
{
homeController.delegate = self;
self.window.rootViewController = homeController;
[self.window makeKeyAndVisible];
}
Then when I click on a button to get back to the split view (where are the root/details view), the app crashes with the above description. I profiled the app with instruments and the line of code responsible of that is located in the RootViewController, in the didSelectRowAtIndexPath method, here is the relevant code:
if(indexPath.row == MenuCustomViewController){
self.customVC=[[CustomViewController alloc] initWithNibName:#"CustomVC"
bundle:nil];
[viewControllerArray addObject:self.customVC];
self.appDelegate.splitViewController.delegate = self.customVC;
}
customVC is a strong property, I tried to allocate directly and assign to the instance variable but that didn't help to fix the crash. Any thoughts ?
EDIT:
Here is the stack trace given by instruments:
[self.appDelegate.splitViewController setViewControllers:viewControllerArray];//this line caused the crash
[viewControllerArray addObject:self.appDescVC];//this statement is called before the above one
self.custinfoVC=[[CustInfoViewController alloc] initWithNibName:#"CustInfo" bundle:nil];//this statement is called before the above one
self.appDelegate.splitViewController.delegate = self.appDescVC;//this statement is called before the above one
custinfoVC and appDescVC are strong properties.
I solved this problem by setting my delegates and datasources to nil in the dealloc method. Not sure if it'll help you but its worth a try.
- (void)dealloc
{
homeController.delegate = nil;
//if you have any table views these would also need to be set to nil
self.tableView.delegate = nil;
self.tableView.dataSource = nil;
}
You may want to setup the CustomViewController during app launch, and display the other views modally on top if necessary. The error message you're getting is because something is getting released by ARC prematurely. It might have not manifested before because pre-arc stuff wasn't always deallocated immediately. ARC is pretty serious about releasing stuff when it leaves scope
Hard to tell without seeing a lot more of the code involved, and what line it breaks on, etc.
This:
- (void) showHome {
//THIS: where is homeController allocated?
homeController.delegate = self;
self.window.rootViewController = homeController;
[self.window makeKeyAndVisible];
}
EDIT:
Add this line right above the line that causes your crash
for (id object in viewControllerArray) {
NSLog(#"Object: %#",object);
}
I had the same Problem.If you are not using "dealloc" method then use "viewWillDisappear" to set nil.
It was difficult to find which delegate cause issue, because it does not indicate any line or code statement for my App So I have try some way to identify delegate, Maybe it becomes helpful to you.
1.Open xib file and from file's owner, Select "show the connections inspector" right hand side menu. Delegates are listed, set them to nil which are suspected.
(Same as my case)Property Object like Textfield can create issue, So set its delegate to nil.
-(void) viewWillDisappear:(BOOL) animated{
[super viewWillDisappear:animated];
if ([self isMovingFromParentViewController]){
self.countryTextField.delegate = nil;
self.stateTextField.delegate = nil;
}
}
I am trying to use the iOS zxing Widget for QR Code Scanning. I have a ViewController which is pushed as an Item in my UINavigationController or presented Modally from another ViewController. This ViewController has a SegmentedControl for 3 different views. Two of those Views are UIWebViews which load simple Websites, nothing special about them.
The selection looks something like this:
- (IBAction)segmentedControlValueChanged:(id)sender {
NSString *urlString;
ZXingWidgetController *widController;
QRCodeReader* qrcodeReader;
NSSet *readers;
switch (segmentedControl.selectedSegmentIndex) {
case 0:
[self.view bringSubviewToFront:self.productSearchWebView];
urlString = [[SACommunicationService sharedCommunicationService] getURLforKey:kURLTypeProductSearch];
[self.productSearchWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]];
break;
case 1:
[self.view bringSubviewToFront:self.marketSearchWebView];
urlString = [[SACommunicationService sharedCommunicationService] getURLforKey:kURLTypeMarketSearch];
[self.marketSearchWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]];
break;
case 2:
widController = [[ZXingWidgetController alloc] initWithDelegate:self showCancel:YES OneDMode:NO];
qrcodeReader = [[QRCodeReader alloc] init];
readers = [[NSSet alloc] initWithObjects:qrcodeReader,nil];
widController.readers = readers;
[self.QRCodeScannerView addSubview:widController.view];
[self.view bringSubviewToFront:self.QRCodeScannerView];
break;
default:
break;
}
}
I tried to debug and go step by step and find out where the problem comes from:
Decoder (which is part of the underlying ZXing logic) tries to call "failedToDecodeImage:" from its delegate (which should be the ZXingWidgetController class) and crashes (EXC_BAD_ACCESS)
While stepping through I found out that the "cancelled" Method of the ZXingWidgetController gets called. Now I don't really know why this method gets called. The Widget shouldn't stop right after initializing and starting the decoder.
So the answer is a very simple one.
I was using iOS 5.0 and ARC. The ZXing ViewController is instantiated locally inside the method. Since the ViewController itself does not get viewed ARC sets a release at the end of the method and the ViewController gets freed. Since the ViewController is released, the view which was retained by the ViewController will be released as well. Cancelled is called because the Main ViewController does not exist anymore and calling some method on a nil pointer results in a BAD_ACCESS.
The Solution here was to set the ZXingViewController as a global strong property. This kept the object from being released right at the end of that method and thus the view which was added as a subview to another ViewControllers view was held in memory as long as the ViewController was alive.
You're not supposed to add controller views as subviews of another view. You're suposed to present controllers using the various UIViewController mechanisms.
By doing what you're doing, you're violating UIViewController contracts. The widget isn't getting things like viewWillAppear, viewDidAppear, etc.
If you want to use ZXing at the UIView/CALayer level instead of the UIViewController level, look at the classes in the ZXing objc directory.
Try this... also in the .h file make this ZXingWidgetController *widController;
And also to the viewScanner set clipToBounds to true.
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self performSelector:#selector(openScanner) withObject:nil afterDelay:0.5];
}
-(void)openScanner
{
self.widController = [[ZXingWidgetController alloc] initMiniWithDelegate:self showCancel:NO OneDMode:YES];
NSMutableSet *readers = [[NSMutableSet alloc ] init];
MultiFormatReader* reader = [[MultiFormatReader alloc] init];
[readers addObject:reader];
self.widController.readers = readers;
[viewScanner addSubview:self.widController.view];
}