美文网首页
SwiftUI MacOS下窗口开启背景拖动与View添加拖动手

SwiftUI MacOS下窗口开启背景拖动与View添加拖动手

作者: O2Space_Xiu | 来源:发表于2022-12-27 17:49 被阅读0次

    在使用SwiftUI 开发 MacOS应用(环境swift 5),需要不仅仅状态栏可拖动窗口移动,设置属性window.isMovableByWindowBackground = true,整个窗口背景内容可拖动窗口移动。目前在窗口下的某个View(SwiftUI)添加拖动手势(DragGesture)能在该窗口某个区域内自由拖动,然而发现在单击拖动过程中,只会拖动窗口移动直至窗口顶到桌面顶部时才触发拖动该View。
    1、调试发现双击该View进行拖动将避开触发窗口的拖动。
    2、同时SwiftUI下一些组件本身区域不会触发窗口背景拖动,比如NavigationLink、Button、List、Table等等,这些组件本身会拦截事件往父视图透传,应该是内部做了处理,SwiftUI无开源无法确定内部逻辑进行效仿。

    测试代码

    TestDragGestureApp.swift

    import SwiftUI
    
    final class AppDelegate: NSObject, NSApplicationDelegate {
        func applicationDidFinishLaunching(_ notification: Notification) {
            if let window = NSApplication.shared.windows.first {
                window.titleVisibility = .hidden
                window.titlebarAppearsTransparent = true
                window.isOpaque = false
                window.backgroundColor = NSColor.white
                window.isMovableByWindowBackground = true // 开启整个窗口背景区域可拖动
            }
        }
    }
    
    @main
    struct TestDragGestureApp: App {
        @NSApplicationDelegateAdaptor(AppDelegate.self) var delegate
        var body: some Scene {
            WindowGroup {
                ContentView()
            }
        }
    }
    }
    

    ContentView.swift

    import SwiftUI
    
    
    struct ContentView : View {
        
        @State var offset: CGSize = .zero //表示视图被拖动的距离
        
        var body: some View {
        
            return VStack{
                canMoveView
            }
            .frame(width: 400, height: 400)
        }
        
        private var canMoveView: some View {
            Circle()
                .fill(Color.orange)
                .frame(width: 200, height: 200)
                .offset(offset)
                .gesture(dragGesture)
        }
        
        private var dragGesture: some Gesture {
            DragGesture()
                .onChanged { (value) in
                    print(value.startLocation, value.location, value.translation)
                    self.offset = value.translation
                }
                .onEnded { (value) in
                    
                }
        }
    }
    

    上方代码 window.isMovableByWindowBackground设置为true 窗口内任意位置单击可拖动Window,然而视图canMoveView拖动手势失效。

    解决方案一

    利用SwiftUI Button组件当作canMoveView的容器进行事件拦截

    ContentView.swift 改造代码区

        private var canMoveView: some View {
            // 解决方案一
            Button { 
    
            } label: {
                Circle()
                    .fill(Color.orange)
                    .frame(width: 200, height: 200)
                    .offset(offset)
                    .gesture(dragGesture)
            }
            .background(.clear)
            .buttonStyle(.plain)
        }
    

    解决方案二

    利用AppKit UI框架,创建NSView子类,重写mouseDownCanMoveWindow Get属性方法返回false,然后将该NSVIew子类转换SwiftUI View,最后将该SwiftUI View设置为目标视图的background视图

    新增CannotMoveWindowMonitorView.swift

    import SwiftUI
    
    // AppKit UI框架下的NSView
    public class CannotMoveWindowMonitorNSView : NSView {
        
        deinit {
            print("deinit(dealloc) ---- CannotMoveWindowMonitorNSView")
        }
        
        public override func draw(_ dirtyRect: NSRect) {
            super.draw(dirtyRect)
            self.wantsLayer = true
            self.layer?.backgroundColor = NSColor.clear.cgColor
            
        }
        
        public override var mouseDownCanMoveWindow: Bool {
            return false
        }
    }
    
    // 将 AppKit UI 转换成 SwiftUI
    public struct CannotMoveWindowMonitorView : NSViewRepresentable {
        
        public func makeNSView(context: Context) -> CannotMoveWindowMonitorNSView {
            let view = CannotMoveWindowMonitorNSView()
            return view
        }
        
        public func updateNSView(_ nsView: CannotMoveWindowMonitorNSView, context: Context) {
        }
    }
    

    新增ViewExtension.swift

    import SwiftUI
    
    extension View {
        @ViewBuilder
        public func mouseDownCannotMoveWindow() -> some View {
            self.background(CannotMoveWindowMonitorView())
        }
    }
    
    

    ContentView.swift 改造代码区

        private var canMoveView: some View {
             Circle()
                .fill(Color.orange)
                .frame(width: 200, height: 200)
                .offset(offset)
                .gesture(dragGesture)
                .mouseDownCannotMoveWindow() // 解决方案二
        }
    

    该方案更加SwiftUI化 后面采用只需要添加.mouseDownCannotMoveWindow()即可

    后记

    1、 注:该问题在SwiftUI iOS下不存在,只存在macOS应用中出现
    2、是否SwiftUI官方已经提供扩展方法,查询相关资料未能找捯
    3、是否还有其他更好的解决方案,请下方留言

    相关文章

      网友评论

          本文标题:SwiftUI MacOS下窗口开启背景拖动与View添加拖动手

          本文链接:https://www.haomeiwen.com/subject/amvoqdtx.html