如何禁用 MacCatalyst 上的全屏按钮

How to disabled fullscreen button on MacCatalyst

目前,我要停止调整大小 window:

#if targetEnvironment(macCatalyst)
windowScene.sizeRestrictions?.minimumSize = CGSize(width: 480, height: 900)
windowScene.sizeRestrictions?.maximumSize = CGSize(width: 480, height: 900)
#endif

let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()

但是 全屏 按钮仍然可以全屏显示。

有点复杂,但有可能。这是一种方法(我删除了所有目标宏以简化 post)。

结果:

代码:

// on next event after UIWindow has made key it is possible to find NSWindow in runtime

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

        ...
        window.makeKeyAndVisible()

        DispatchQueue.main.async { // < wait for NSWindow available
            SilentBridge.disableCloseButton(for: self.nsWindow(from: window))
        }
    }

// added helper function to SceneDelegate to find NSWindow

    func nsWindow(from window: UIWindow) -> NSObject? {
        guard let nsWindows = NSClassFromString("NSApplication")?.value(forKeyPath: "sharedApplication.windows") as? [NSObject] else { return nil }
        for nsWindow in nsWindows {
            let uiWindows = nsWindow.value(forKeyPath: "uiWindows") as? [UIWindow] ?? []
            if uiWindows.contains(window) {
                return nsWindow
            }
        }
        return nil
    }

Objective-C 部分是首选(使用未声明的选择器更简单)。通过 Xcode 模板添加新的 Objective-C class 并确认创建网桥。之后需要在生成的 *-Bridging-Header.h 中添加以下 class 头文件,一切都应该有效。

// SilentBridge.h

@import Foundation;

@interface SilentBridge : NSObject
+ (void)disableCloseButtonFor:(NSObject  * _Nullable)window;
@end

// SilentBridge.m

#import "SilentBridge.h"
@import Foundation;

// Forward declarations to allow direct calls in below method
@interface NSObject(SilentBridge) 
- (id)standardWindowButton:(NSInteger)value;
- (void)setEnabled:(BOOL)flag;
@end

@implementation SilentBridge

+ (void)disableCloseButtonFor:(NSObject *)window {
    if ([window respondsToSelector:@selector(standardWindowButton:)]) {
        id closeButton = [window standardWindowButton:2];
        if ([closeButton respondsToSelector:@selector(setEnabled:)]) {
            [closeButton setEnabled:NO];
        }
    }
}
@end

这是另一种不需要 Objective-C、选择器或异步调用的方法。它也不需要目标宏,iOS 将简单地跳过 if let NSApplication。将它粘贴到首先出现的视图控制器中。请注意,这会禁用所有 windows 上的绿色全屏按钮。如果您想区分,请使用 Asperi 的 Swift 部分的想法。

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    func bitSet(_ bits: [Int]) -> UInt {
        return bits.reduce(0) { [=10=] | (1 << ) }
    }

    func property(_ property: String, object: NSObject, set: [Int], clear: [Int]) {
        if let value = object.value(forKey: property) as? UInt {
            object.setValue((value & ~bitSet(clear)) | bitSet(set), forKey: property)
        }
    }

    // disable full-screen button
    if  let NSApplication = NSClassFromString("NSApplication") as? NSObject.Type,
        let sharedApplication = NSApplication.value(forKeyPath: "sharedApplication") as? NSObject,
        let windows = sharedApplication.value(forKeyPath: "windows") as? [NSObject]
    {
        for window in windows {
            let resizable = 3
            property("styleMask", object: window, set: [], clear: [resizable])
            let fullScreenPrimary = 7
            let fullScreenAuxiliary = 8
            let fullScreenNone = 9
            property("collectionBehavior", object: window, set: [fullScreenNone], clear: [fullScreenPrimary, fullScreenAuxiliary])
        }
    }
}