Как отключить полноэкранную кнопку на MacCatalyst - PullRequest
2 голосов
/ 15 января 2020

UIWindow on macCatalyst with close, minimize and fullscreen buttons

В настоящее время у меня есть это, чтобы остановить изменение размера окна:

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

, но кнопка fullscreen делает его полным экран в любом случае.

Ответы [ 2 ]

1 голос
/ 02 марта 2020

Вот еще один подход, который не требует 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) { $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])
        }
    }
}
1 голос
/ 15 января 2020

Это немного сложно, но возможно. Вот подход (я упустил все целевые макросы, чтобы упростить публикацию).

Результат:

enter image description here

Код:

// 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
    }

Цель - C часть предпочтительна (просто проще работать с необъявленными селекторами). Добавьте новый класс Objective- C через шаблон Xcode и подтвердите создание моста. После этого необходимо добавить ниже сгенерированный файл заголовка класса *-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
...