AVMutableVideoComposition - CoreAnimation: warning, deleted thread with uncommitted CATransaction - xcode

I am working on some basic video compositions using AVMutableComposition - currently a video layer (AVMutableVideoComposition) with a text layer (CATextLayer).
It all looks ok but when I let it export via "AVMutableComposition exportAsynchronouslyWithCompletionHandler " it goes off and completes but returns this error:
CoreAnimation: warning, deleted thread with uncommitted CATransaction; created by:
0 QuartzCore 0x00007fff8a106959 _ZN2CA11Transaction4pushEv + 219
1 QuartzCore 0x00007fff8a106531 _ZN2CA11Transaction15ensure_implicitEv + 273
2 QuartzCore 0x00007fff8a10d66f _ZN2CA5Layer13thread_flags_EPNS_11TransactionE + 37
3 QuartzCore 0x00007fff8a10d5a7 _ZN2CA5Layer4markEPNS_11TransactionEjj + 79
4 QuartzCore 0x00007fff8a112cac _ZN2CA5Layer27contents_visibility_changedEPNS_11TransactionEb + 216
5 QuartzCore 0x00007fff8a112b65 _ZN2CA5Layer12mark_visibleEPNS_11TransactionEb + 261
6 QuartzCore 0x00007fff8a112b26 _ZN2CA5Layer12mark_visibleEPNS_11TransactionEb + 198
7 QuartzCore 0x00007fff8a112b26 _ZN2CA5Layer12mark_visibleEPNS_11TransactionEb + 198
8 QuartzCore 0x00007fff8a1128d1 _ZN2CA5Layer11set_visibleEj + 335
9 QuartzCore 0x00007fff8a1126b9 _ZN2CA7Context9set_layerEPKv + 75
10 MediaToolbox 0x00007fff857f155b FigCoreAnimationRendererInvalidate + 108
11 CoreFoundation 0x00007fff8ec763df CFRelease + 511
12 MediaToolbox 0x00007fff857d3a6b FigVideoCompositionProcessorInvalidate + 675
13 MediaToolbox 0x00007fff85791341 FigAssetWriterCreateWithURL + 18573
14 MediaToolbox 0x00007fff85791f7b FigAssetWriterCreateWithURL + 21703
15 CoreMediaAuthoringCrunchers 0x00000001046e2b99 AssetAudioSourcer_CreateInstance + 3865
I find that this goes away if I comment-out the following line - however the CATextLayer is not rendered:
videoComposition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
Any thoughts anyone?
Adam

You must ensure any UI drawing occurs on the main thread. The line you commented out probably does some drawing internally, so you want to make sure this code is executed on the main thread. A couple ways to do this is by using the function dispatch_async() or the methods performSelectorOnMainThread:withObject:waitUntilDone: or performSelectorOnMainThread:withObject:waitUntilDone:modes:
- (void) someMethod
{
// You may need to load a container object to pass as myCustomData whose
// contents you then access in myCustomDrawing: if the data isn't accessible
// as instance data.
[...]
// Perform all drawing/UI updates on the main thread.
[self performSelectorOnMainThread:#selector(myCustomDrawing:)
withObject:myCustomData
waitUntilDone:YES];
[...]
}
- (void) myCustomDrawing:(id)myCustomData
{
// Perform any drawing/UI updates here.
videoComposition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer
inLayer:parentLayer];
}
For a related post on the difference between dispatch_async() and performSelectorOnMainThread:withObjects:waitUntilDone: see Whats the difference between performSelectorOnMainThread and dispatch_async on main queue?

Related

Disable Autolayout for NSSplitView in Interface Builder

By using a NSSplitViewController inside my Storyboard, I wanted to override the behaviour of the contained split view.
By implementing some of the the NSSplitViewDelegate method regarding the max constraint for the slider, I got this exception raised:
2017-01-12 11:05:45.873814 iCache[3399:659731] [General] SplitViewController's splitView is unable to use autolayout because the SplitViewController overrides an incompatible delegate method.
2017-01-12 11:05:45.874089 iCache[3399:659731] [General] (
0 CoreFoundation 0x00007fffb5d9ee7b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x00007fffca989cad objc_exception_throw + 48
2 CoreFoundation 0x00007fffb5da3b82 +[NSException raise:format:arguments:] + 98
3 Foundation 0x00007fffb77edd50 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
4 AppKit 0x00007fffb389ca29 -[NSSplitView _splitViewUseConstraintBasedLayout] + 355
5 AppKit 0x00007fffb389c894 -[NSSplitView(NSSplitViewDividerViews) _canUseDividerViewsAsSubviews] + 74
6 AppKit 0x00007fffb389c0f7 -[NSSplitView(NSSplitViewDividerViews) _updateDividerViews] + 36
7 AppKit 0x00007fffb389dd41 -[NSSplitViewController _updateSplitView:withBlock:] + 51
8 AppKit 0x00007fffb389dc89 -[NSSplitViewController viewDidLoad] + 144
9 AppKit 0x00007fffb3896283 -[NSViewController _sendViewDidLoad] + 97
10 CoreFoundation 0x00007fffb5d17889 -[NSSet makeObjectsPerformSelector:] + 217
11 AppKit 0x00007fffb3814902 -[NSIBObjectData nibInstantiateWithOwner:options:topLevelObjects:] + 1389
12 AppKit 0x00007fffb391d436 -[NSNib _instantiateNibWithExternalNameTable:options:] + 696
13 AppKit 0x00007fffb391d06a -[NSNib _instantiateWithOwner:options:topLevelObjects:] + 143
14 AppKit 0x00007fffb403f34a -[NSStoryboard instantiateControllerWithIdentifier:] + 234
15 AppKit 0x00007fffb3805bb7 NSApplicationMain + 780
16 iCache 0x00000001000127f4 main + 84
17 libdyld.dylib 0x00007fffcb26d255 start + 1
)
Apparently this is due to to Autolayout used for the split view.
Is there a way to disable Autolayout for this NSSplitView inside Interface Builder?
Enable/disable Auto Layout
You can disable Auto Layout per XIB file in the Interface Builder under File Inspector.
You should be able to control autolayout per view through the views translatesAutoresizingMaskIntoConstraints.
Why it doesn't work
According to OS X 10.11 release notes there are some methods you cannot use in combination with autolayout.
Please see this excerption from the notes.
Auto Layout NSSplitView improvements
In 10.8, NSSplitView properly respects constraints applied to its subviews, such as their minimum view widths. There are also new APIs for controlling the holding priorities, which determine both the NSLayoutPriority at which a split view holds its sizes and also which views change size if the split view itself grows or shrinks.
(NSLayoutPriority)holdingPriorityForSubviewAtIndex:(NSInteger)subviewIndex;
(void)setHoldingPriority:(NSLayoutPriority)priority forSubviewAtIndex:(NSInteger)subviewIndex;
In order to take advantage of these improvements, you must NOT implement any of the following NSSplitViewDelegate methods:
splitView:constrainMinCoordinate:ofSubviewAt:
splitView:constrainMaxCoordinate:ofSubviewAt:
splitView:resizeSubviewsWithOldSize:
splitView:shouldAdjustSizeOfSubview:
These methods are incompatible with auto layout. You can typically achieve their effects and more with auto layout.

How to update an NSTextView while user is scrolling (without crashing)

I'm using an NSTextView to display the result of a long search, where lines are added as they are found by a background thread using
[self performSelectorOnMainThread: #selector(addMatch:)
withObject:options waitUntilDone:TRUE];
As the update routine I have
-(void)addMatch:(NSDictionary*)options{
...
NSTextStorage* store = [textView textStorage];
[store beginEditing];
[store appendAttributedString:text];
...
[store endEditing];
}
This works fine, until the user scrolls through the matches as they are being updated, at which point there's an exception
-[NSLayoutManager _fillLayoutHoleForCharacterRange:desiredNumberOfLines:isSoft:] *** attempted layout while textStorage is editing. It is not valid to
cause the layoutManager to do layout while the textStorage is editing
(ie the textStorage has been sent a beginEditing message without a
matching endEditing.)
within a layout call:
0 CoreFoundation 0x00007fff92ea364c __exceptionPreprocess + 172
1 libobjc.A.dylib 0x00007fff8acd16de objc_exception_throw + 43
2 CoreFoundation 0x00007fff92ea34fd +[NSException raise:format:] + 205
3 UIFoundation 0x00007fff8fe4fbc1 -[NSLayoutManager(NSPrivate) _fillLayoutHoleForCharacterRange:desiredNumberOfLines:isSoft:] + 641
4 UIFoundation 0x00007fff8fe5970c _NSFastFillAllLayoutHolesForGlyphRange + 1493
5 UIFoundation 0x00007fff8fda8821 -[NSLayoutManager lineFragmentRectForGlyphAtIndex:effectiveRange:] + 39
6 AppKit 0x00007fff8ef3cb02 -[NSTextView _extendedGlyphRangeForRange:maxGlyphIndex:drawingToScreen:] + 478
7 AppKit 0x00007fff8ef3ba97 -[NSTextView drawRect:] + 1832
8 AppKit 0x00007fff8eed9a09 -[NSView(NSInternal) _recursive:displayRectIgnoringOpacity:inGraphicsContext:CGContext:topView:shouldChangeFontReferenceColor:] + 1186
9 AppKit 0x00007fff8eed9458 __46-[NSView(NSLayerKitGlue) drawLayer:inContext:]_block_invoke + 218
10 AppKit 0x00007fff8eed91f1 -[NSView(NSLayerKitGlue) _drawViewBackingLayer:inContext:drawingHandler:] + 2407
11 AppKit 0x00007fff8eed8873 -[NSView(NSLayerKitGlue) drawLayer:inContext:] + 108
12 AppKit 0x00007fff8efaafd2 -[NSTextView drawLayer:inContext:] + 179
13 AppKit 0x00007fff8ef22f76 -[_NSBackingLayerContents drawLayer:inContext:] + 145
14 QuartzCore 0x00007fff9337c177 -[CALayer drawInContext:] + 119
15 AppKit 0x00007fff8ef22aae -[_NSTiledLayer drawTile:inContext:] + 625
16 AppKit 0x00007fff8ef227df -[_NSTiledLayerContents drawLayer:inContext:] + 169
17 QuartzCore 0x00007fff9337c177 -[CALayer drawInContext:] + 119
18 AppKit 0x00007fff8f6efd64 -[NSTileLayer drawInContext:] + 169
19 QuartzCore 0x00007fff9337b153 CABackingStoreUpdate_ + 3306
20 QuartzCore 0x00007fff9337a463 ___ZN2CA5Layer8display_Ev_block_invoke + 59
21 QuartzCore 0x00007fff9337a41f x_blame_allocations + 81
22 QuartzCore 0x00007fff93379f1c _ZN2CA5Layer8display_Ev + 1546
23 AppKit 0x00007fff8ef226ed -[NSTileLayer display] + 119
24 AppKit 0x00007fff8ef1ec34 -[_NSTiledLayerContents update:] + 5688
25 AppKit 0x00007fff8ef1d337 -[_NSTiledLayer display] + 375
26 QuartzCore 0x00007fff93379641 _ZN2CA5Layer17display_if_neededEPNS_11TransactionE + 603
27 QuartzCore 0x00007fff93378d7d _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 35
28 QuartzCore 0x00007fff9337850e _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 242
29 QuartzCore 0x00007fff93378164 _ZN2CA11Transaction6commitEv + 390
30 QuartzCore 0x00007fff93388f55 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 71
31 CoreFoundation 0x00007fff92dc0d87 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
32 CoreFoundation 0x00007fff92dc0ce0 __CFRunLoopDoObservers + 368
33 CoreFoundation 0x00007fff92db2f1a __CFRunLoopRun + 1178
34 CoreFoundation 0x00007fff92db2838 CFRunLoopRunSpecific + 296
35 UIFoundation 0x00007fff8fdfe744 -[NSHTMLReader _loadUsingWebKit] + 2097
36 UIFoundation 0x00007fff8fdffb55 -[NSHTMLReader attributedString] + 22
37 UIFoundation 0x00007fff8fe12cca _NSReadAttributedStringFromURLOrData + 10543
38 UIFoundation 0x00007fff8fe10306 -[NSAttributedString(NSAttributedStringUIFoundationAdditions) initWithData:options:documentAttributes:error:] + 115
What is wrong, given that everything is between beginEditing and endEditing?
From the stack trace (which is not complete), it looks like a run loop source is firing at an inopportune moment.
NSAttributedString uses WebKit to parse HTML. WebKit sometimes runs the run loop. For the general case, it may need to fetch resources from the network to render properly. Since that takes time, it runs the run loop to wait for the result and process other things at the same time.
One of the other run loop sources seems to be a Core Animation source to do the next step in some animation (scrolling the text view, presumably).
You didn't show all of the code between beginEditing and endEditing. I suspect you have constructed an NSAttributedString from HTML or data fetched from a URL in between those two places. That allows the Core Animation run loop source to fire. That asks the text view to draw, which asks its layout manager to lay out the text. This is occurring after beginEditing but before endEditing, which is the cause for the exception.
So, try reordering your code to construct all NSAttributedStrings before beginEditing.
And file a bug with Apple. In my opinion, when NSAttributeString uses WebKit to render HTML, it needs to make WebKit use a private run loop mode so no other sources can fire. They may prefer a different solution, but the bug is real.
As far as I can tell, there's no fix for this. An alternative that works is storing the matches as attributed strings in an array, and using an NSTableView to show the matches by setting the textField.attributedStringValue (calling reloadData every time you add a new match); something like this (where matchContent is an NSMutableArray):
-(void)addMatch:(NSDictionary*)options{
...
[matchContent addObject:text];
[resultTableView reloadData];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return matchContent.count;
}
- (NSView *)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row {
NSTableCellView *result = [tableView makeViewWithIdentifier:#"MyView" owner:self];
result.textField.attributedStringValue = [matchContent objectAtIndex:row];
return result;
}
If the result is multi-line, you may also need to check the autoresizing mask of the cell/text field, and return the row height for the table view using the attributed string's boundingRectWithSize method.

Core Data throws NSInternalInconsistencyException "The context is still dirty after 100 attempts."

I struggle finding cause of the following exception. It occurs from time to time with no clear pattern I can repeat to reproduce problem.
Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'Failed to process pending changes before save.
The context is still dirty after 100 attempts.
Typically this recursive dirtying is caused by a bad validation method,
-willSave, or notification handler.'
The application stores and process tracking data and deals with only one entity:
#interface CSTrackingEntry : NSManagedObject
#property (nonatomic, retain) NSString * data;
#property (nonatomic, retain) NSDate * dateRecorded;
#end
CSTracking entries are read, created and deleted in batches. Few up to dozen in a batch, once every couple of minutes. There is no notification handlers registered.
UPDATE: Stack captured
2012-02-03 10:26:11.121 BatteryNurse[17162:1803] An uncaught exception was raised
2012-02-03 10:26:11.121 BatteryNurse[17162:1803] Failed to process pending changes
before save. The context is still dirty after 100 attempts. Typically this recursive
dirtying is caused by a bad validation method, -willSave, or notification handler.
2012-02-03 10:26:11.264 BatteryNurse[17162:1803] *** Terminating app due to uncaught
exception 'NSInternalInconsistencyException', reason: 'Failed to process pending changes
before save. The context is still dirty after 100 attempts. Typically this recursive
dirtying is caused by a bad validation method, -willSave, or notification handler.'
*** Call stack at first throw:
(
0 CoreFoundation 0x00007fff8183f784 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x00007fff89306f03 objc_exception_throw + 45
2 CoreData 0x00007fff8543a654 -[NSManagedObjectContext(_NSInternalChangeProcessing) _prepareForPushChanges:] + 244
3 CoreData 0x00007fff8543a0af -[NSManagedObjectContext save:] + 207
4 BatteryNurse 0x0000000100075ee6 __40-[CSTrackingEntry(Methods) deleteObject]_block_invoke_0 + 102
5 BatteryNurse 0x000000010007514f __42-[CSCoreDataKernel(CoreData) executeSync:]_block_invoke_0 + 79
6 libSystem.B.dylib 0x00007fff869bcfbb dispatch_barrier_sync_f + 79
7 BatteryNurse 0x00000001000750ee -[CSCoreDataKernel(CoreData) executeSync:] + 110
8 BatteryNurse 0x0000000100075e6f -[CSTrackingEntry(Methods) deleteObject] + 175
9 CoreFoundation 0x00007fff817ff123 -[NSArray makeObjectsPerformSelector:] + 499
10 BatteryNurse 0x000000010003a13f -[CSTracker(PrivateMethods) processAndSendBundlesToServer] + 383
11 BatteryNurse 0x00000001000393a4 __23-[CSTracker flushAsync]_block_invoke_0 + 420
12 libSystem.B.dylib 0x00007fff869c3d64 _dispatch_call_block_and_release + 15
13 libSystem.B.dylib 0x00007fff869a28d2 _dispatch_queue_drain + 251
14 libSystem.B.dylib 0x00007fff869a2734 _dispatch_queue_invoke + 57
15 libSystem.B.dylib 0x00007fff869a22de _dispatch_worker_thread2 + 252
16 libSystem.B.dylib 0x00007fff869a1c08 _pthread_wqthread + 353
17 libSystem.B.dylib 0x00007fff869a1aa5 start_wqthread + 13
You are modifying the object while it's being saved. If you're observing NSManagedObjectContextObjectsDidChange (which will be posted as part of the save, but before the actual saving happens), and you change objects are a result of that notification, you'll create a loop:
When you call -save: CoreData first calls -processPendingChanges: (if there are changes). As part of that CoreData sends out an NSManagedObjectContextObjectsDidChange notification. If additional objects changed while the notification was being processed, it calls -processPendingChanges: again, sends out the notification again, etc. Once there are no pending changes, CoreData persists the objects to the store. If pending changes keep appearing CoreData will eventually give up and print
The context is still dirty after 100 attempts.
That's the gist of it.

Reusing NSArrayController in multiple places

I have a simple NSArrayController hooked up to one of my coreData models. I can easily show all the items in a NSTableView without problem, however, if I try to bind a NSTableColumn containing a NSPopUpButtonCell to the arrangedObjects on the same controller, I get a nasty exception thrown at me. (If I bind to a NSPopupButton that is outside the table, however, everything seems to work correctly)
Catchpoint 2 (throw)2011-12-18 16:00:07.251 MyApp[6050:707] -[MyCoreDataModel count]: unrecognized selector sent to instance 0x10045aea0
Catchpoint 2 (exception thrown).2011-12-18 16:01:32.901 MyApp[6050:707] -[MyCoreDataModel count]: unrecognized selector sent to instance 0x10045aea0
2011-12-18 16:01:32.907 MyApp[6050:707] (
0 CoreFoundation 0x00007fff9191e286 __exceptionPreprocess + 198
1 libobjc.A.dylib 0x00007fff93bd6d5e objc_exception_throw + 43
2 CoreFoundation 0x00007fff919aa4ce -[NSObject doesNotRecognizeSelector:] + 190
3 CoreFoundation 0x00007fff9190b133 ___forwarding___ + 371
4 CoreFoundation 0x00007fff9190af48 _CF_forwarding_prep_0 + 232
5 AppKit 0x00007fff8b5bbad3 -[_NSSelectionBinderPlugin populateObject:withContent:valueKey:objectKey:insertsNullPlaceholder:] + 235
6 AppKit 0x00007fff8b45242e -[NSSelectionBinder _adjustObject:mode:observedController:observedKeyPath:context:editableState:adjustState:] + 678
7 AppKit 0x00007fff8b56f11a -[NSValueBinder updateTableColumnDataCell:forDisplayAtIndex:] + 145
8 AppKit 0x00007fff8b5af502 -[_NSBindingAdaptor tableColumn:willDisplayCell:row:] + 112
9 AppKit 0x00007fff8b0a00b1 -[NSTableView _tryCellBasedMouseDown:atRow:column:withView:] + 498
10 AppKit 0x00007fff8b062a4c -[NSTableView mouseDown:] + 1072
11 AppKit 0x00007fff8afc90e0 -[NSWindow sendEvent:] + 6306
12 AppKit 0x00007fff8af6168f -[NSApplication sendEvent:] + 5593
13 AppKit 0x00007fff8aef7682 -[NSApplication run] + 555
14 AppKit 0x00007fff8b17680c NSApplicationMain + 867
15 InstaLibrary 0x0000000100001732 main + 34
16 InstaLibrary 0x0000000100001704 start + 52
Why is this the case? How come you can't reuse the array controller in multiple places?
How come you can't reuse the array controller in multiple places?
That's not what the error message says. What the error message says is that you tried to bind an array property to a property whose value is not an array:
2011-12-18 16:01:32.901 MyApp[6050:707] -[MyCoreDataModel count]: unrecognized selector sent to instance 0x10045aea0
Meaning “I tried to treat this as an array (send it count), but it wasn't an array (and so didn't respond to count), and that's a problem”.
You've already identified the problem binding, so now you simply need to fix it. For more specific advice, you'll need to edit your question to include at least one screenshot of the binding in question, showing what object is it on, which binding is it, what object did you bind to, what controller key did you set, and what model key path did you set.

Override NSWindow close button

I'm working on a application for OS X where I use a custom window that draws an image as the background, including the title bar. I've been modifying this code to draw the window, and then calling [NSWindow standardWindowButton:forStyleMask:] to get the standard close, minimize, and maximize buttons.
The problem is my application uses NSPopovers, and when I close or minimize the application while a popover is open, it will close the popover or show an animation of the popover minimizing instead of closing the application. Is there a way to override the default close/minimize behavior in an NSWindow so I can dismiss any open popovers first?
Thanks, and sorry if this is an obvious question — this is my first time working with the OS X SDK so I don't have a lot of experience.
EDIT: A few hours after I posted this I thought I had an obvious solution – use the NSWindowDelegate methods "windowWillClose:" and "windowWillMiniaturize:" and dismiss the popovers there. However, it seems that since the close/minimize buttons are closing the popover, that if a popover is open, these delegate methods won't be called. This takes me back to step 1, but hopefully knowing the behavior will help someone figure out the problem.
There's also another problem in with the NSPopovers and I don't know if it's connected or not, so I thought I would add it here just in case there was a common cause. Sometimes, when I attempt to dismiss a popover, I'll get this error (for context, I'm pressing an NSButton that calls a function that checks for the existence of the popover, and if it exists, closes it):
2011-08-30 11:24:08.949 Playground[11194:707] *** Assertion failure in +[NSView _findFirstKeyViewInDirection:forKeyLoopGroupingView:], /SourceCache/AppKit/AppKit-1138/AppKit.subproj/NSView.m:11026
2011-08-30 11:24:08.950 Playground[11194:707] this method is supposed to only be invoked on top level items
2011-08-30 11:24:08.958 Playground[11194:707] (
0 CoreFoundation 0x00007fff873d4986 __exceptionPreprocess + 198
1 libobjc.A.dylib 0x00007fff87ac6d5e objc_exception_throw + 43
2 CoreFoundation 0x00007fff873d47ba +[NSException raise:format:arguments:] + 106
3 Foundation 0x00007fff8950314f -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 169
4 AppKit 0x00007fff88211064 +[NSView _findFirstKeyViewInDirection:forKeyLoopGroupingView:] + 137
5 AppKit 0x00007fff87d1f546 _replacementKeyViewAlongKeyViewPath + 565
6 AppKit 0x00007fff87d1f2ff -[NSView nextValidKeyView] + 179
7 AppKit 0x00007fff87d1f199 -[NSWindow _selectFirstKeyView] + 714
8 AppKit 0x00007fff882361cf _NSWindowRecursiveFindFirstResponder + 164
9 AppKit 0x00007fff882395c8 _NSWindowExchange + 79
10 AppKit 0x00007fff883a7e3a -[_NSWindowTransformAnimation startAnimation] + 426
11 AppKit 0x00007fff87c98bb2 -[NSWindow _doOrderWindow:relativeTo:findKey:forCounter:force:isModal:] + 592
12 AppKit 0x00007fff87c9890f -[NSWindow orderWindow:relativeTo:] + 154
13 AppKit 0x00007fff883dfaf0 _NSPopoverCloseAndAnimate + 948
14 Playground 0x00000001000078a4 -[MainWindowController dismissPopover:] + 100
15 Playgorund 0x0000000100007012 -[MainWindowController requestWasClicked:] + 98
16 CoreFoundation 0x00007fff873c411d -[NSObject performSelector:withObject:] + 61
17 AppKit 0x00007fff87ca2852 -[NSApplication sendAction:to:from:] + 139
18 AppKit 0x00007fff87ca2784 -[NSControl sendAction:to:] + 88
19 AppKit 0x00007fff87ca26af -[NSCell _sendActionFrom:] + 137
20 AppKit 0x00007fff87ca1b7a -[NSCell trackMouse:inRect:ofView:untilMouseUp:] + 2014
21 AppKit 0x00007fff87d2157c -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] + 489
22 AppKit 0x00007fff87ca0786 -[NSControl mouseDown:] + 786
23 AppKit 0x00007fff87c6b66e -[NSWindow sendEvent:] + 6280
24 AppKit 0x00007fff87c03f19 -[NSApplication sendEvent:] + 5665
25 AppKit 0x00007fff87b9a42b -[NSApplication run] + 548
26 AppKit 0x00007fff87e1852a NSApplicationMain + 867
27 Playground 0x0000000100001c52 main + 34
28 Playground 0x0000000100001c24 start + 52
29 ??? 0x0000000000000001 0x0 + 1
)
The solution turned out to be fairly simple.
When I created the button on the NSWindow, I changed the action and target of the button:
[closeButton setTarget:self.delegate]; // alternatively you can make it self.windowController
[closeButton setAction:#selector(closeThisWindow:)];
And then in the NSWindowController subclass, I implemented the method:
-(void)closeThisWindow {
[self close]; // for the minimize button you'll call [self.window miniaturize]
}
For some reason, the NSPopovers always reappear when the window is reopened or unminimized; since I actually want this behavior in my application, it's not a problem, but it's something to keep in mind if you use this solution. If you don't have any child windows, then you can just iterate through self.window.childWindows because NSPopovers are considered child windows. If you have other child windows you want to handle separately, you could add an array to the NSWindow subclass that monitors all of your popovers and just iterate through that.

Resources