2

macCatalyst 上的 UIWindow,带有关闭、最小化和全屏按钮

目前,我有这个停止调整窗口大小:

#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()

但是全屏按钮无论如何都会使其全屏显示。

4

2 回答 2

7

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

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

    func bitSet(_ bits: [Int]) -> UInt {
        return bits.reduce(0) { $0 | (1 << $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])
        }
    }
}
于 2020-03-02T10:05:16.847 回答
1

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

结果:

在此处输入图像描述

代码:

// 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 类并确认创建桥。之后需要在生成的类头文件中添加下面的文件*-Bridging-Header.h,一切都应该工作。

// 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
于 2020-01-15T20:11:21.673 回答