I'm developing an Audio Unit with a custom view, setting ARC when building it in XCode. When It runs in LogicX everything is fine except after having closed the window of my view. When I try to (re)open it, it crashes in :
void EventListenerDispatcher (void *inRefCon,
void *inObject,
const AudioUnitEvent *inEvent,
UInt64 inHostTime,
Float32 inValue)
{
myCustomView* SELF = (__bridge myCustomView*)inRefCon;
[SELF jaEventListener:inObject
event:inEvent
value:inValue];
}
with a Bad_Access error on the "myCustomView* SELF = ..." line . I suppose that ARC has released the view and, when reopened, the view is not valid anymore.
If so, how can I make the view not be released by ARC? Could there be another reason it crashes?
Related
I have an iPhone app that displays files to the user (videos, pdf files and documents). When you click on a file thumbnail in my FileViewController I call UIDocumentInteractionController to present the file to the user. All app screens are locked to portrait orientation apart from the document controller, that can be rotated to view landscape. This has all worked fine in previous releases, but it has suddenly changed and broken, I presume when iOS 8 was released.
Now, if I play a video and rotate it to landscape and hit done, the video is removed from the screen and the FileViewController nib that called the document the view is cut off half way down the screen.
I've done some testing and have printed out the view bounds width and height. Testing on an iPhone 4S, when I first load the FileViewController the width and height is 320 x 480, correct.
When I play a video in landscape mode and hit done, the underlaying view is automatically rotated back to portrait as landscape isn't support in this view, but when printing out the screen bounds the width is 480 and height 320. It still thinks the app is in landscape even though it's been moved back to portrait. The screen itself is the right way round, just cut off after 320 pixels!
I've been trying to manipulate the orientation of alsorts of controls but I can't see what's even generating the issue, let alone fixing it.
This is how I have implemented by orientations:
The App has been set up to support ALL orientations.
I have subclasses the tab bar controller to specify the following:
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
Within FileViewController I call the following code:
- (void) Show_File:(File *)file
{
NSURL *targetURL = [NSURL fileURLWithPath:filePath];
UIDocumentInteractionController *document = [UIDocumentInteractionController interactionControllerWithURL: targetURL];
document.delegate = self;
[document presentPreviewAnimated: YES];
}
- (UIViewController *)documentInteractionControllerViewControllerForPreview: (UIDocumentInteractionController *) controller
{
AppDelegate *theDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
return theDelegate.fileNavController.childViewControllerForStatusBarHidden;
}
Any ideas? I've search around but I can't find anyone having this particular issue. Thanks.
Just for anyone else experiencing this issue, I raised a support ticket with Apple and apparently it is an iOS bug....
"This is a bug QLPreviewController (which is used by UIDocumentInteractionController to display previews)"
I have a work around which is working ok for now. In my subclass of UITabBarController I've incldued this code:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (self.transitionCoordinator && self.presentedViewController.presentationController {
self.view.frame = self.presentedViewController.presentationController.containerView.bounds;
}
}
I have a very strange (& serious) problem.
My app uses a UIDocumentInteractionController to share a PDF document.
When the user selects the "Mail" option in the controller's pop-up the MailCompose window is opened.
But, neither the Send nor Cancel button in this window causes the MailCompose window to be dismissed, meaning the user gets stuck and has to kill the app. The mail does go out though.
Here's the catch:
This happens only in iOS8 (both versions released so far) and only on apps installed via the AppStore. That EXACT same version of the app, when running on my device via USB debugging works fine.
Here's some code:
-(void)sharePDF:(id)sender
{
#try
{
NSURL *fileURL = [NSURL fileURLWithPath:currentFileObject.LocalPath];
if(fileURL)
{
//UIDocumentInteractionController
NSString *newPath;
#try
{
//Create a copy of the file for sharing with a friendly name
if (currentFileObject.isSpecialReport)
{
newPath = [svc saveReport:[NSData dataWithContentsOfURL:fileURL] ToFile:[NSString stringWithFormat:#"%#.pdf", currentFileObject.ReportName]];
}
else
{
newPath = [svc saveReport:[NSData dataWithContentsOfURL:fileURL] ToFile:[NSString stringWithFormat:#"%#.pdf", currentFileObject.PatientFullName]];
}
}
#catch (NSException *exception) {
return;
}
NSURL *newURL = [NSURL fileURLWithPath:newPath];
self.docController = [UIDocumentInteractionController interactionControllerWithURL:newURL];
self.docController.delegate = self;
if (currentFileObject.isSpecialReport)
{
self.docController.name = [NSString stringWithFormat:#"Pathology - %#", currentFileObject.ReportName];
}
else
{
self.docController.name = [NSString stringWithFormat:#"Pathology - %#", currentFileObject.PatientFullName];
}
[self.docController presentOptionsMenuFromBarButtonItem:btnShare animated:YES];
}
}
#catch (NSException *exception) {
return;
}
}
I do not implement any of the delegate methods since non of them are required, I also do not make use of the preview functionality.
What's most puzzling to me is that the app from the AppStore behaves differently than my local one although the code is identical. My next step is to use the new beta developer tools (Test Flight) to re-publish the app, hoping that I can replicate the problem.
EDIT: I found a similar question on SO here: Cannot dismiss email sheet invoked from UIDocumentInteractionController in iOS 8
After reading that post I think it worth mentioning that I submitted the app to the AppStore via XCode 5 (the last version before XCode 6). Can that really be a factor here? Does Apple not use the same version on their side as the version in which the app was originally built?
I think this is a bug in iOS 8, and if it's still not working for you, I don't think Apple are likely to fix it. I'd upgrade to Xcode 6 and see if that fixes it for you. (It did for us, as you've discovered).
Seeing this error message in the logs, though not consistently, around the time that I use SLComposeViewController to open a Twitter or Facebook share sheet. I am not using any new iOS 8 API, just testing existing code on iOS 8. I see others have had this problem and even seen crashes when using other modal view controllers from the Cocoa Touch SDK.
LaunchServices: invalidationHandler called
Are there new precautions to take with SLComposeViewController and UIActivityViewController in iOS 8? Something else to consider?
Add this code after you present your activity view controller:
if ([activityVC respondsToSelector:#selector(popoverPresentationController)])
{
// iOS 8+
UIPopoverPresentationController *presentationController = [activityVC popoverPresentationController];
presentationController.sourceView = sender; // if button or change to self.view.
}
Looking at the developer forums: "That log message does not indicate any error on your part."
I had a similar problem with a UIDocumentInteractionController, where when I tapped outside it to dismiss it, or selected another app to open the document in, it would crash with the "LaunchServices: invalideationHandler called" console message displayed twice (only using iOS 8).
A workaround is to add the call to presentOpenInMenuFromRect:inView:animated to the main queue, i.e.
dispatch_async(dispatch_get_main_queue(), ^() {
[self.documentInteraction presentOpenInMenuFromRect:theRect inView:self.view animated:YES];
});
You may also need to define the sourceRect. I used the following code to display a SLComposeViewController from a tableView.
if ([controller respondsToSelector:#selector(popoverPresentationController)]) {
//get rect for this row in table
CGRect frame = [self.tableView rectForRowAtIndexPath:indexPath];
//convert table row frame to view reference
CGRect frameInView = [self.tableView convertRect:frame toView:self.view];
[controller popoverPresentationController].sourceRect = frameInView;
[controller popoverPresentationController].sourceView = self.view;
}
Regarding the auto-closing (not the crash):
I think it's probably related to the link you are trying to share. I'm seeing the same thing when trying to post music links (Spotify, SoundCloud,...). The same tweet works if I replace the link by a link to some non-media-content. I'll file radar on this to see whether it's intentional...
This gets rid of the Error message for me and works as expected. You have to get rid of the if statement that calls "isAvailableForServiceType:"
It should look like this. Happy coding.
SLComposeViewController *tweetSheet = [SLComposeViewController
composeViewControllerForServiceType:SLServiceTypeTwitter];
[tweetSheet setInitialText:#"Great fun to learn iOS programming at appcoda.com!"];
[self presentViewController:tweetSheet animated:YES completion:nil];
if ([tweetSheet respondsToSelector:#selector(popoverPresentationController)])
{
// iOS 8+
UIPopoverPresentationController *presentationController = [tweetSheet popoverPresentationController];
presentationController.sourceView = sender; // if button or change to self.view.
}
I Currently have a nearly landscape only iPad application on the app store and have been having some issues with the new iOS 6 way on handling rotation locking.
It is a UINavigationController based application and since iOS handles most the responsibility to the rootViewController of the UIWindow I have to manually ask each UIViewController what rotation it wants.
As I have a very large amount of UIViewControllers manually adding code to each Controller to do this would have taken me ages, So I made an extension of both the UINavigationController and UIViewController to override these calls and there I could manually set what views I want to block Portrait to and what ones to allow it to.
UINavigationController-Extension.m:
//
// UINavigationController-Extension.m
// DrivingInstructor
//
// Created by Liam Nichols on 06/12/2012.
// Copyright (c) 2012 Liam Nichols. All rights reserved.
//
#import "UINavigationController-Extension.h"
#implementation UINavigationController (Extension)
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return [self.topViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
-(NSUInteger)supportedInterfaceOrientations
{
return self.topViewController.supportedInterfaceOrientations;
}
-(BOOL)shouldAutorotate
{
return YES;
}
#end
#implementation UIViewController (Extension)
-(BOOL)shouldAutorotate
{
return NO;
}
-(NSUInteger)supportedInterfaceOrientations
{
if ([[self portraitClasses] containsObject:NSStringFromClass([self class])])
{
return UIInterfaceOrientationMaskAll;
}
return UIInterfaceOrientationMaskLandscape;
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
if ([[self portraitClasses] containsObject:NSStringFromClass([self class])])
{
return YES;
}
return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
-(NSArray*)portraitClasses
{
static NSArray *classes;
if (classes == nil)
{
classes = #[ #"MockTestController", #"PLUILibraryViewController", #"PhotoViewController" ];
}
return classes;
}
#end
At fist I thought this had fixed the issue (I also kept the app locked to landscape in the info.plist so that it would launch to lanscape and then in the app delegate I called application:supportedInterfaceOrientationsForWindow: and returned all orientations so that my select views could access portrait if needed.
So this seemed to have worked and all view controllers where locked to lanscape accept the 3 I specified in my extension class.. I was monitoring the extension and whenever I pushed to the new controller it checked for the orientations and locked the app to the specified orientation.
The one issue I found however and can't seem to fix is that when i for example am in portrait on my allowed view controller and try to pop back to the previous view controller what is locked to landscape, supportedInterfaceOrientations is never called again and the view that should be locked to landscape isn't (this causes issues).
As per apples documents, this is how it should work as the responsibility of handling rotation is passed to the rootViewController and as the user isn't rotating their device and the rootViewController isn't changing there is no need to request supportedInterfaceOrientations..
My question is, is there a way to get the application to force call supportedInterfaceOrientations or should I be doing this differently?
Thanks for reading, and If I can find a solution to this last bug then this code might be a good reference to people who are also in the same situation.
-----Edit-----
Was doing some further investigation and found that just before the viewWillAppear: function, supportedInterfaceOrientations is actually in fact called on the controller I am attempting to pop back to and does return the correct mask UIInterfaceOrientationMaskLandscape to try and make it automatically rotate back from portrait however it doesn't seem to listen to this response and still leaves the UIViewController in portrait...
So this means that I do not need to call the supportedInterfaceOrientations again but instead make the device rotate back round to landscape!
According to the documentation you can call:
+ (void)attemptRotationToDeviceOrientation
which if I understand the documentation correctly then would query rotation to the different view controllers again.
I have a simple game that renders 2D graphics to a frame buffer (not using any OpenGL). I was going to use a CVDisplayLink to get a clean framerate, however most examples on the web deal with OpenGL or QuickTime.
So far I have a sub class of NSView:
#interface GameView : NSView {
#private
CVDisplayLinkRef displayLink;
}
- (CVReturn)getFrameForTime:(const CVTimeStamp*)outputTime;
#end
And I set up the CVDisplayLink callback:
CVDisplayLinkSetOutputCallback(displayLink, MyDisplayLinkCallback, self);
And I have the callback function:
CVReturn MyDisplayLinkCallback (CVDisplayLinkRef displayLink,
const CVTimeStamp *inNow,
const CVTimeStamp *inOutputTime,
CVOptionFlags flagsIn,
CVOptionFlags *flagsOut,
void *displayLinkContext)
{
CVReturn error = [(GameView*)displayLinkContext getFrameForTime:inOutputTime];
return error;
}
The part where I'm stuck is what to do in getFrameForTime: to draw to the graphics context in the GameView. My first guess was to do the drawing the same way you would in drawRect:
- (CVReturn)getFrameForTime:(const CVTimeStamp*)outputTime
{
CGContextRef ctxCurrent = [[NSGraphicsContext currentContext] graphicsPort];
//.. Drawing code follows
}
But ctxCurrent is nil which I think I understand - normally there is some setup that happens before the drawRect: that makes your view the current context. I think this is the part I'm missing. How do I get the context for my view?
Or am I going about this in all the wrong ways?
You could leave your drawing code in ‑drawRect: and then in your MyDisplayLinkCallback() set an ivar to the current time and call ‑display on your view. This will force your view to immediately redraw itself.
In your ‑drawRect: method, just use the value of the time ivar to do whatever drawing is necessary to update the view appropriately for the current animation frame.
You should create a separate -draw: method, and call that from your MyDisplayLinkCallback() as well as from -drawRect:.
I found Rob Keniger's response to my liking and tried it out; sadly, if you call -display from your display link callback your app may hang in a deadlock when you terminate the app, leaving you to force-quit the app (for me it happened more often than not).