【问题标题】:MacOS Catalyst: how to allow double click on UIView to zoom window?MacOS Catalyst:如何允许双击 UIView 来缩放窗口?
【发布时间】:2021-07-08 01:22:53
【问题描述】:

默认情况下,在 MacOS 中,您可以双击窗口的标题栏来缩放它(调整大小以适应屏幕 - 不同于最大化)。再次双击它会恢复到之前的大小。

这也适用于我的 Catalyst 应用程序。但是我需要隐藏标题栏,并且需要在该标题栏区域中为我自己的自定义 UIView 提供双击行为。我可以使用它来隐藏它:

#if os(OSX) || os(macOS) || targetEnvironment(macCatalyst)
    UIApplication.shared.connectedScenes.forEach({
        if let titlebar = ($0 as? UIWindowScene)?.titlebar {
            titlebar.titleVisibility = .hidden
            titlebar.toolbar = nil
        }
    })
#endif

有没有一种方法可以让我切换窗口缩放?

【问题讨论】:

  • 好吧NSWindowsetIsZoomed,但我没有看到UIWindowSceneUIWindow 有类似的东西。
  • @Alexander 是的,我想通了。我能够找出我在下面发布的答案的解决方案。基本上使用插件包,然后从 iOS 应用程序调用它。

标签: ios swift macos cocoa mac-catalyst


【解决方案1】:

我能够使用一种解决方法来解决这个问题。 UIKit/Catalyst 本身不提供任何方法来做到这一点。但是我可以在这篇文章中使用第二种方法大纲

如何从 Mac Catalyst 应用程序访问 AppKit API

https://betterprogramming.pub/how-to-access-the-appkit-api-from-mac-catalyst-apps-2184527020b5

我使用了第二种方法而不是第一种,因为第一种似乎是私有 API(我可能是错的)并且会在 App Store 中被拒绝。使用插件包并在其上调用方法的第二种方法对我来说效果很好。通过这种方式,我不仅能够执行缩放,还能够执行其他 MacOS Appkit 功能,例如监听键盘、鼠标滚动、悬停检测等。

创建插件包后,这是我在插件中的代码:

插件.swift:

import Foundation
@objc(Plugin)
protocol Plugin: NSObjectProtocol {
    init()
    func toggleZoom()
    func macOSStartupStuff()
}

MacPlugin.swift:

import AppKit
class MacPlugin: NSObject, Plugin {
    required override init() {}

    func macOSStartupStuff() {
        NSApplication.shared.windows.forEach({
            $0.titlebarAppearsTransparent = true
            $0.titleVisibility = .hidden
            $0.backgroundColor = .clear
            ($0.contentView?.superview?.allSubviews.first(where: { String(describing: type(of: $0)).hasSuffix("TitlebarDecorationView") }))?.alphaValue = 0
        })
    }

    func toggleZoom(){
        NSApplication.shared.windows.forEach({
            $0.performZoom(nil)
        })
    }
}

extension NSView {
    var allSubviews: [NSView] {
        return subviews.flatMap { [$0] + $0.allSubviews }
    }
}

然后我从我的 iOS 应用代码中调用它。这会在顶部添加一个透明视图,双击调用插件代码以切换缩放。

请注意,您必须从 viewDidAppear 或在窗口已初始化和呈现时的某个地方调用它。否则将无法正常工作。

#if os(OSX) || os(macOS) || targetEnvironment(macCatalyst)
    @objc func zoomTapped(){
        plugin?.toggleZoom()
    }
    
    var pluginWasLoaded = false
    lazy var plugin : Plugin? = {
        pluginWasLoaded = true
        if let window = (UIApplication.shared.delegate as? AppDelegate)?.window {
            let transparentTitleBarForDoubleClick = UIView(frame: .zero)
            let tapGesture = UITapGestureRecognizer(target: self, action: #selector(zoomTapped))
            tapGesture.numberOfTapsRequired = 2
            transparentTitleBarForDoubleClick.addGestureRecognizer(tapGesture)
            transparentTitleBarForDoubleClick.isUserInteractionEnabled = true
            
            transparentTitleBarForDoubleClick.backgroundColor = .clear
            transparentTitleBarForDoubleClick.translatesAutoresizingMaskIntoConstraints = false
            window.addSubview(transparentTitleBarForDoubleClick)
            window.bringSubviewToFront(transparentTitleBarForDoubleClick)
            
            window.addConstraints([
                                NSLayoutConstraint(item: transparentTitleBarForDoubleClick, attribute: .leading, relatedBy: .equal, toItem: window, attribute: .leading, multiplier: 1, constant: 0),
                                 NSLayoutConstraint(item: transparentTitleBarForDoubleClick, attribute: .top, relatedBy: .equal, toItem: window, attribute: .top, multiplier: 1, constant: 0),
                                 NSLayoutConstraint(item: transparentTitleBarForDoubleClick, attribute: .trailing, relatedBy: .equal, toItem: window, attribute: .trailing, multiplier: 1, constant: 0),
                                 transparentTitleBarForDoubleClick.bottomAnchor.constraint(equalTo: window.safeTopAnchor)
                                 ])
            window.layoutIfNeeded()
        }
        
        guard let bundleURL = Bundle.main.builtInPlugInsURL?.appendingPathComponent("MacPlugin.bundle") else { return nil }
        guard let bundle = Bundle(url: bundleURL) else { return nil }
        guard let pluginClass = bundle.classNamed("MacPlugin.MacPlugin") as? Plugin.Type else { return nil }
        return pluginClass.init()
    }()
#endif

从 viewDidAppear 调用它:

override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        #if os(OSX) || os(macOS) || targetEnvironment(macCatalyst)
        if !Singleton.shared.pluginWasLoaded {
            Singleton.shared.plugin?.macOSStartupStuff()
        }
        #endif
}

【讨论】:

  • 很酷的解决方案,拥有这个“逃生舱”似乎真的很方便。即使有一个本地 SwiftUI API 来执行此操作,这也是一种在您了解它之前畅通无阻的好方法。
  • @Alexander 肯定的。它基本上允许您使用 MacOS appkit 做任何您想做的事情。至于从 app kit 调用 iOS 代码,我最终使用了 NotificationCenter。这有点麻烦,但很有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-07
  • 1970-01-01
  • 1970-01-01
  • 2017-05-11
  • 1970-01-01
  • 2016-02-27
  • 1970-01-01
相关资源
最近更新 更多