Created
September 27, 2023 10:28
-
-
Save macmade/3b576903c7dffbf78265446cdf1a325c to your computer and use it in GitHub Desktop.
ClipToBoundsFix.m
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************************************************************* | |
* The MIT License (MIT) | |
* | |
* Copyright (c) 2023 DigiDNA - www.imazing.com | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
* THE SOFTWARE. | |
******************************************************************************/ | |
@import Cocoa; | |
@import ObjectiveC.runtime; | |
/******************************************************************************* | |
* ABOUT THIS HACK: | |
* | |
* As of macOS 14 Sonoma, Apple decided to change the default value of NSView's | |
* clipToBounds property from true to false: | |
* | |
* In macOS 14, AppKit is exposing the clipsToBounds property of NSView. | |
* For applications linked against the macOS 14 SDK, the default value of | |
* this property is false. Apps linked against older SDKs default to true. | |
* Some classes, like NSClipView, continue to default to true. The change | |
* in defaults recognizes that it is vastly easier to reason about clipping | |
* a view’s descendants than it is to unclip a view’s ancestors. | |
* | |
* It means the rect passed to the draw method is no longer constrained to the | |
* view bounds by default, as it was since Mac OS X 10.0... | |
* This obviously leads to insane issues with custom views. | |
* | |
* This hack restores the previous behavior, ensuring clipToBounds is set to | |
* true by default. | |
* | |
* I won't comment on the decision to change such a default value, basically | |
* breaking all apps with custom views as I don't like to swear in source code | |
* comments... But if you're reading this, you can probably feel my pain... | |
******************************************************************************/ | |
NS_ASSUME_NONNULL_BEGIN | |
@interface NSView( ClipToBoundsFixer ) | |
- ( instancetype )initWithFrame_ClipToBoundsFix: ( NSRect )frame; | |
- ( instancetype )initWithCoder_ClipToBoundsFix: ( NSCoder * )coder; | |
@end | |
NS_ASSUME_NONNULL_END | |
@implementation NSView( ClipToBoundsFixer ) | |
- ( instancetype )initWithFrame_ClipToBoundsFix: ( NSRect )frame | |
{ | |
if( ( self = [ self initWithFrame_ClipToBoundsFix: frame ] ) ) | |
{ | |
self.clipsToBounds = YES; | |
} | |
return self; | |
} | |
- ( instancetype )initWithCoder_ClipToBoundsFix: ( NSCoder * )coder | |
{ | |
if( ( self = [ self initWithCoder_ClipToBoundsFix: coder ] ) ) | |
{ | |
self.clipsToBounds = YES; | |
} | |
return self; | |
} | |
@end | |
static void init( void ) __attribute__( ( constructor ) ); | |
static void init( void ) | |
{ | |
if( @available( macOS 14, * ) ) | |
{ | |
Class cls = [ NSView class ]; | |
Method initWithFrame1 = class_getInstanceMethod( cls, @selector( initWithFrame: ) ); | |
Method initWithCoder1 = class_getInstanceMethod( cls, @selector( initWithCoder: ) ); | |
Method initWithFrame2 = class_getInstanceMethod( cls, @selector( initWithFrame_ClipToBoundsFix: ) ); | |
Method initWithCoder2 = class_getInstanceMethod( cls, @selector( initWithCoder_ClipToBoundsFix: ) ); | |
method_exchangeImplementations( initWithFrame1, initWithFrame2 ); | |
method_exchangeImplementations( initWithCoder1, initWithCoder2 ); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you for that 🙏🏻