Why is the adjustScroll: method being called twice per scroll in NSScrollView? - cocoa

I have overridden the adjustScroll: method within the documentView of my NSScrollView in order to investigate how to use it to constrain large scroll movements.
What I am seeing is that for each scroll "event" prior to calling the normal scrolling notifications (which I am listening to elsewhere) it calls the adjustScroll method twice(!) and the first time it always seems to call it with a rectangle of size (1,1) - strange?
My method is as follows:
override func adjustScroll(newVisible: NSRect) -> NSRect {
println("Document: adjustScroll: \(newVisible)")
return super.adjustScroll(newVisible)
}
With the results of the println being as follows:
Document: adjustScroll: (1.0, 0.0, 1.0, 1.0)
Document: adjustScroll: (1.0, 0.0, 1436.0, 855.0)
I only have one "Document" object in my view hierarchy and there is only one scrollView...perplexed.
EDIT
I have printed out the stack within the adjustScroll call. The relevant parts are as follows for each of the two calls:
(1.0,1.0)
[0 Grid 0x000000010007d697 _TToFC4Grid8Document12adjustScrollfS0_FVSC6CGRectS1_ + 87,
1 AppKit 0x00007fff84368070 -[_NSScrollingConcurrentMainThreadSynchronizer _scrollToCononicalOrigin] + 655,
2 AppKit 0x00007fff84367a51 -[_NSScrollingConcurrentMainThreadSynchronizer _synchronize:completionHandler:] + 167,
3 AppKit 0x00007fff84367963 __80-[_NSScrollingConcurrentMainThreadSynchronizer initWithSharedData:constantData:]_block_invoke + 144,
4 libdispatch.dylib 0x000000010057ed43 _dispatch_client_callout + 8,
...
(1436.0, 855.0)
[0 Grid 0x000000010007d697 _TToFC4Grid8Document12adjustScrollfS0_FVSC6CGRectS1_ + 87,
1 AppKit 0x00007fff84206f8d -[NSClipView _scrollTo:animateScroll:flashScrollerKnobs:] + 1302,
2 AppKit 0x00007fff84368209 -[_NSScrollingConcurrentMainThreadSynchronizer _scrollToCononicalOrigin] + 1064,
3 AppKit 0x00007fff84367a51 -[_NSScrollingConcurrentMainThreadSynchronizer _synchronize:completionHandler:] + 167,
4 AppKit 0x00007fff84367963 __80-[_NSScrollingConcurrentMainThreadSynchronizer initWithSharedData:constantData:]_block_invoke + 144,
5 libdispatch.dylib 0x000000010057ed43 _dispatch_client_callout + 8,
...

Related

Movement algorithm with friction

For example, I have an object that follows mouse horizontally, its minimum position is 100 and the maximum is 200.
Within this range, it follows mouse linearly, meaning 1 unit of mouse position equals to 1 unit of object position.
Now, if the mouse is outside of the range (below 100, or above 200), I want it to start applying friction, so the object gracefully stops the further it is from range, for example:
Mouse position | Object position
200 200
220 205
240 209
260 212
280 215
300 217
320 218
340 219
360 220
380 220
400 220
...
I managed to implement it like this within mouse-move handler:
if (mousePosition > 200 || mousePosition < 100) {
delta = mousePosition - objectPosition;
objectPosition += delta * 0.25; // 0.25 if friction factor
}
But is there a better way? How to implement function outside of mouse-move handler:
getObjectPosition(mousePosition) {
return // ???
}
The question is language agnostic.
You can calculate your the object position as a piecewise function of the mouse position:
getObjectPosition(mousePosition) {
if(mousePosition < 100)
return 100 - friction(100 - mousePosition);
if(mousePosition > 200)
return 200 + friction(mousePosition - 200);
return mousePosition;
}
Where friction is a function to calculate the "soft" function you want. A simple one would be:
friction(x) {
return 0.25*x;
}
which would reduce the object speed by 0.25 when its beyond the [100, 200] bounds. Your function seems to approach some kind of asymptote though. You can achieve it with, for example, a quadratic:
friction(x) {
M = 160;
x = min(x, M);
return x*(1 - x/(2*M));
}
Here the object will gradually slow down, and stop completely when the mouse is outside the [-60, 360] interval.

Why is rotating an NSImageView crashing my app?

I'm making a macOS app and I've got this code used to rotate an image:
didSet {
let degreesToRotate = oldValue - phoneXRotation
phoneImageView.rotate(byDegrees: CGFloat(degreesToRotate))
degreesLabel.isHidden = false
degreesLabel.stringValue = "\(phoneXRotation)"
if phoneXRotation > 70 {
statusLabel.stringValue = "Phone orientation: Right Tilt"
} else if phoneXRotation < -70 {
statusLabel.stringValue = "Phone orientation: Left Tilt"
} else {
statusLabel.stringValue = "Phone orientation: Flat"
}
}
The app will randomly crash in an lldb error. If I comment out the third line that rotate the phone, I have no problems. The conversion from Int to CGFloat shouldn't crash. Any ideas?
Here is the stack trace:
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
frame #0: 0x00007fff8989b44e AppKit`-[NSApplication _crashOnException:] + 109
frame #1: 0x00007fff8996ea32 AppKit`__37+[NSDisplayCycle currentDisplayCycle]_block_invoke.31 + 708
frame #2: 0x00007fff8ba62d37 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
frame #3: 0x00007fff8ba62ca7 CoreFoundation`__CFRunLoopDoObservers + 391
frame #4: 0x00007fff8ba436d9 CoreFoundation`__CFRunLoopRun + 873
frame #5: 0x00007fff8ba43114 CoreFoundation`CFRunLoopRunSpecific + 420
frame #6: 0x00007fff8afa3ebc HIToolbox`RunCurrentEventLoopInMode + 240
frame #7: 0x00007fff8afa3cf1 HIToolbox`ReceiveNextEventCommon + 432
frame #8: 0x00007fff8afa3b26 HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter + 71
frame #9: 0x00007fff8953ca54 AppKit`_DPSNextEvent + 1120
frame #10: 0x00007fff89cb87ee AppKit`-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 2796
frame #11: 0x00007fff895313db AppKit`-[NSApplication run] + 926
frame #12: 0x00007fff894fbe0e AppKit`NSApplicationMain + 1237
* frame #13: 0x000000010000691d Dataspeed Mac Project`main at AppDelegate.swift:12
frame #14: 0x00007fffa11f1235 libdyld.dylib`start + 1
frame #15: 0x00007fffa11f1235 libdyld.dylib`start + 1

Using SKTexture:withImageNamed in a for loop to load animations crashes after playing 30 + animation

My code below creates a Texture from an image name from file lookups in a plist like (Duck1_1,Duck1_2,Duck1_3,etc) it loops through them then adds them to a mutable array. The mutable array is then used to animate with the textures stored and played with an SKAction. This works fine for the animations until 30-40 are played then it crashes and says too many files open and it can't open the files anymore. Any ideas on how to stop this from happening?
thanks
//loadPlistForAnimationWithNames function
NSMutableArray *animationToReturn = [NSMutableArray array];
for(NSString *frameNumber in animationFrameNumbers) {
NSString *frameName = [NSString stringWithFormat:#"%#%#.png",animationFramePrefix,frameNumber];
if(frameName!=nil)
[animationToReturn addObject:[SKTexture textureWithImageNamed:frameName]];
}
SKAction *action=nil;
action=[SKAction animateWithTextures:animationToReturn timePerFrame:animationDelay resize:NO restore:NO];
SKAction *repeatAction=nil;
repeatAction = [SKAction repeatAction:action count:1];
[animationToReturn removeAllObjects];
action=nil;
animationFrameNumbers=nil;
return repeatAction;
//Call to loadPlistForAnimationWithNames and run animation
SKAction *action=[animalObject loadPlistForAnimationWithNames:#"duckAnim1" andClassName:NSStringFromClass([self class])];
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Duck1_1"];
sprite.position=CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
sprite.size=self.frame.size;
[sprite setZPosition:3];
[self addChild:sprite];
[sprite runAction:action];
Here is the Xcode error I get when it crashes:
: ImageIO: CGImageReadCreateDataWithMappedFile 'open' failed '/private/var/mobile/Containers/Bundle/Application/782E98C3-0452-43EF-BF4D-401773D5FDCD/WildNoizesLite.app/Duck1_1.png'
error = 24 (Too many open files)
Backtrace:
* thread #1: tid = 0x301c4, 0x00000001944ab270 libsystem_kernel.dylib__pthread_kill + 8, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
frame #0: 0x00000001944ab270 libsystem_kernel.dylib__pthread_kill + 8
frame #1: 0x0000000194549170 libsystem_pthread.dylibpthread_kill + 112
frame #2: 0x0000000194422b18 libsystem_c.dylibabort + 112
frame #3: 0x00000001934b5418 libc++abi.dylibabort_message + 116
frame #4: 0x00000001934d4b8c libc++abi.dylibdefault_terminate_handler() + 304
frame #5: 0x0000000193ce83c0 libobjc.A.dylib_objc_terminate() + 128
frame #6: 0x00000001934d1bb4 libc++abi.dylibstd::__terminate(void (*)()) + 16
frame #7: 0x00000001934d173c libc++abi.dylib__cxa_rethrow + 144
frame #8: 0x0000000193ce8294 libobjc.A.dylibobjc_exception_rethrow + 44
frame #9: 0x0000000181ef5384 CoreFoundationCFRunLoopRunSpecific + 572
frame #10: 0x000000018b94b6fc GraphicsServicesGSEventRunModal + 168
frame #11: 0x0000000186af2f40 UIKitUIApplicationMain + 1488
* frame #12: 0x0000000100174fa0 Wild Noizesmain(argc=1, argv=0x000000016fd6f9d8) + 124 at main.m:16
frame #13: 0x0000000194392a08 libdyld.dylib`start + 4

App freezes when resizing window?

I'm working on a menubar application and I've made a custom (borderless) NSWindow which I've added programmatically. For some reason when I try to resize the window, it freezes and I'm unable to resize the window and open the menu-bar menu. However I can still move the window but not click things in it. The weird thing is that there is no error message at all, even though I turned on breakpoints and NSZombieMode. So I've no idea what the problem could be.
// customWindow.m
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)aStyle
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)flag {
// Using NSBorderlessWindowMask results in a window without a title bar.
self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask | NSResizableWindowMask backing:NSBackingStoreBuffered defer:NO];
if (self != nil) {
if (![[SharedClass sharedManager] lastColor]) {
[self setBackgroundColor:[NSColor colorWithRed:(float)252/255 green:(float)249/255 blue:(float)151/255 alpha:1]];
[[SharedClass sharedManager] setLastColor:self.backgroundColor];
} else {
[self setBackgroundColor: [[SharedClass sharedManager] lastColor]];
}
[self setLevel:NSNormalWindowLevel];
// Start with no transparency for all drawing into the window
// Turn off opacity so that the parts of the window that are not drawn into are transparent.
[self setOpaque:NO];
// [self setBackgroundColor:[NSColor clearColor]];
[self setHasShadow:YES];
[self setMovableByWindowBackground:YES];
[self makeKeyAndOrderFront:theTextView];
panel = [NSColorPanel sharedColorPanel];
[panel setTarget:self];
[panel setColor:self.backgroundColor];
[panel setAction:#selector(colorUpdate:)];
[panel setShowsAlpha:YES];
[self setDelegate:self];
NSRect cFrame = [self frame];
NSView *theView = [[NSView alloc] initWithFrame:cFrame];
[self setContentView:theView];
[self setMinSize:CGSizeMake(100, 100)];
}
return self;
}
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize {
exit.frame = NSMakeRect(2,(frameSize.height-12), 10, 10);
palette.frame = NSMakeRect((frameSize.width-24),(frameSize.height-12), 10, 10);
locker.frame = NSMakeRect((frameSize.width-12),(frameSize.height-12), 10, 10);
theTextView.frame = NSRectFromCGRect(CGRectMake(0, 8, frameSize.width, (frameSize.height-20)));
return frameSize;
}
- (void)windowDidResize:(NSNotification *)notification{
NSLog(#"resized");
}
Stack trace :
0 DDHotKey 0x00000001000044ed -[CustomWindow windowDidResize:] + 61
1 CoreFoundation 0x00007fff9034245c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
2 CoreFoundation 0x00007fff90232634 _CFXNotificationPost + 3140
3 Foundation 0x00007fff8d7f59d1 -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
4 AppKit 0x00007fff89117770 -[NSWindow _setFrameCommon:display:stashSize:] + 2885
5 AppKit 0x00007fff891d4b73 -[NSWindow setFrame:display:animate:] + 641
6 DDHotKey 0x000000010000a8c0 -[DDHotKeyAppDelegate setFrame:animated:] + 416
7 DDHotKey 0x00000001000091ae __53-[DDHotKeyAppDelegate applicationDidFinishLaunching:]_block_invoke + 2318
8 AppKit 0x00007fff895e5233 GlobalObserverHandler + 117
9 HIToolbox 0x00007fff88d0ab6c _ZL23DispatchEventToHandlersP14EventTargetRecP14OpaqueEventRefP14HandlerCallRec + 1260
10 HIToolbox 0x00007fff88d09fae _ZL30SendEventToEventTargetInternalP14OpaqueEventRefP20OpaqueEventTargetRefP14HandlerCallRec + 386
11 HIToolbox 0x00007fff88d09e22 SendEventToEventTargetWithOptions + 43
12 HIToolbox 0x00007fff88d46a9e _ZL29ToolboxEventDispatcherHandlerP25OpaqueEventHandlerCallRefP14OpaqueEventRefPv + 1762
13 HIToolbox 0x00007fff88d0b295 _ZL23DispatchEventToHandlersP14EventTargetRecP14OpaqueEventRefP14HandlerCallRec + 3093
14 HIToolbox 0x00007fff88d09fae _ZL30SendEventToEventTargetInternalP14OpaqueEventRefP20OpaqueEventTargetRefP14HandlerCallRec + 386
15 HIToolbox 0x00007fff88d1fcb6 SendEventToEventTarget + 40
16 AppKit 0x00007fff890d71a2 _DPSNextEvent + 3001
17 AppKit 0x00007fff890d5f68 -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 346
18 AppKit 0x00007fff890cbbf3 -[NSApplication run] + 594
19 AppKit 0x00007fff89048354 NSApplicationMain + 1832
20 DDHotKey 0x0000000100007312 main + 34
21 libdyld.dylib 0x00007fff8bd335c9 start + 1

Cocoa event loop blocks while tracking a menu

I maintain a legacy OSX application that instead of calling NSApplicationMain() to process events, runs an event loop on the main thread that looks like this:
...
do {
event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
inMode: NSDefaultRunLoopMode dequeue:YES];
[self handleEvent:event];
}
} while(event != nil);
...
Everything works until I activate the application's menu. Once the menu is active, nextEventMatchingMask blocks forever. I can see in lldb that osx starts another modal event loop (carbon?) which causes the cococa's one to block:
frame #2: 0x00007fff92748eb4 CoreFoundation`__CFRunLoopServiceMachPort + 212
frame #3: 0x00007fff9274837b CoreFoundation`__CFRunLoopRun + 1371
frame #4: 0x00007fff92747bd8 CoreFoundation`CFRunLoopRunSpecific + 296
frame #5: 0x00007fff8c11c56f HIToolbox`RunCurrentEventLoopInMode + 235
frame #6: 0x00007fff8c11c2ea HIToolbox`ReceiveNextEventCommon + 431
frame #7: 0x00007fff8c17568a HIToolbox`AcquireNextEventInMode + 54
frame #8: 0x00007fff8c172e2b HIToolbox`IsUserStillTracking(MenuSelectData*, unsigned char*) + 173
frame #9: 0x00007fff8c172a41 HIToolbox`TrackMenuCommon(MenuSelectData&, unsigned char*) + 1661
frame #10: 0x00007fff8c1803c1 HIToolbox`MenuSelectCore(MenuData*, Point, double, unsigned int, OpaqueMenuRef**, unsigned short*) + 510
frame #11: 0x00007fff8c1800fe HIToolbox`_HandleMenuSelection2 + 446
frame #12: 0x00007fff8a9a8de0 AppKit`_NSHandleCarbonMenuEvent + 277
frame #13: 0x00007fff8a8dfd0d AppKit`_DPSNextEvent + 1828
frame #14: 0x00007fff8a8def68 AppKit`-[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 346
Is there anything I can do to prevent this behaviour ?
Unfortunately I cannot rewrite the application so I need a quick and dirty solution to run the main thread while the menu is active.

Resources