admin管理员组

文章数量:1026989

I want to create a solution that tells me when the user has finished resizing a view. Currently, I’m using a timer for this, but I’m not satisfied with my approach because it doesn’t feel like idiomatic SwiftUI coding. I’m looking for a more native way to detect the final size of the view at the end of resizing. As we change the window size our view size would change.

import SwiftUI

struct ContentView: View {
    
    @State private var resizeTimer: Timer? = nil
    
    var body: some View {
        GeometryReader { geometryValue in
            Color.white
                .onChange(of: geometryValue.size) { newValue in

                    // Invalidate any existing timer
                    resizeTimer?.invalidate()

                    // Start a new timer to detect when resizing ends
                    resizeTimer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: false) {  _ in // [weak self]
                        didEndResizing(size: newValue)
                    }
                }
        }
        .padding()
    }
    
    private func didEndResizing(size: CGSize) {
        print("View resizing ended. Final size: \(size)")
    }
}

I want to create a solution that tells me when the user has finished resizing a view. Currently, I’m using a timer for this, but I’m not satisfied with my approach because it doesn’t feel like idiomatic SwiftUI coding. I’m looking for a more native way to detect the final size of the view at the end of resizing. As we change the window size our view size would change.

import SwiftUI

struct ContentView: View {
    
    @State private var resizeTimer: Timer? = nil
    
    var body: some View {
        GeometryReader { geometryValue in
            Color.white
                .onChange(of: geometryValue.size) { newValue in

                    // Invalidate any existing timer
                    resizeTimer?.invalidate()

                    // Start a new timer to detect when resizing ends
                    resizeTimer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: false) {  _ in // [weak self]
                        didEndResizing(size: newValue)
                    }
                }
        }
        .padding()
    }
    
    private func didEndResizing(size: CGSize) {
        print("View resizing ended. Final size: \(size)")
    }
}
Share Improve this question edited Nov 16, 2024 at 21:20 swiftPunk asked Nov 16, 2024 at 16:52 swiftPunkswiftPunk 1 4
  • How would the user resize the view? Views in general cannot be resized by the user unless you deliberately provide a mechanism for resizing it. Do you mean the window instead? – Sweeper Commented Nov 16, 2024 at 16:59
  • @swiftPunk How is a view supposed to know intrinsically when a size change is finished if the input for the change comes from external events like user inputs? Maybe I don't understand your question and you mean that you give a view a target size from the outside and the size of your view changes several times on the way to this target size? – ITGuy Commented Nov 16, 2024 at 19:12
  • @Sweeper: window, as we change the window size our view size would change. – swiftPunk Commented Nov 16, 2024 at 20:59
  • 1 @LeoDabus I disagree with the duplicate closure. The first reason is that the duplicate target is using AppKit and this question is about SwiftUI. While you can use the same approach as the answer in the duplicate target to solve this in SwiftUI, there might be other SwiftUI-specific ways (perhaps in the future). The second reason is that for a SwiftUI app with multiple windows, the didEndLiveResizeNotification approach also involves getting the current NSWindow, which is not trivial to do. – Sweeper Commented Nov 19, 2024 at 19:05
Add a comment  | 

1 Answer 1

Reset to default 1

To detect the end of a window resizing session, you can observe the NSWindow.didEndLiveResizeNotification.

.onReceive(NotificationCenter.default.publisher(for: NSWindow.didEndLiveResizeNotification)) { notification in
    print("Resize did end!")
}

notification.object tells you which NSWindow it is that got resized, so if your app has multiple windows, you can check that to respond only to resizes of the view's own window. For example:

struct ContentView: View {
    @State private var myWindow: NSWindow?
    var body: some View {
        Color.blue
            .background { WindowAccessor(window: $myWindow) }
            .onReceive(NotificationCenter.default.publisher(for: NSWindow.didEndLiveResizeNotification)) { notification in
                if (notification.object as? NSWindow) == myWindow {
                    print("Did resize!")
                }
            }
    }
}

struct WindowAccessor: NSViewRepresentable {
    @Binding var window: NSWindow?
    
    class MoveToWindowDetector: NSView {
        var onMoveToWindow: (NSWindow?) -> Void = { _ in }
        
        override func viewDidMoveToWindow() {
            onMoveToWindow(window)
        }
    }

    func makeNSView(context: Context) -> MoveToWindowDetector {
        MoveToWindowDetector()
    }

    func updateNSView(_ nsView: MoveToWindowDetector, context: Context) {
        nsView.onMoveToWindow = { window = $0 }
    }
}

WindowAccessor is slightly modified from Asperi's answer here.

I want to create a solution that tells me when the user has finished resizing a view. Currently, I’m using a timer for this, but I’m not satisfied with my approach because it doesn’t feel like idiomatic SwiftUI coding. I’m looking for a more native way to detect the final size of the view at the end of resizing. As we change the window size our view size would change.

import SwiftUI

struct ContentView: View {
    
    @State private var resizeTimer: Timer? = nil
    
    var body: some View {
        GeometryReader { geometryValue in
            Color.white
                .onChange(of: geometryValue.size) { newValue in

                    // Invalidate any existing timer
                    resizeTimer?.invalidate()

                    // Start a new timer to detect when resizing ends
                    resizeTimer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: false) {  _ in // [weak self]
                        didEndResizing(size: newValue)
                    }
                }
        }
        .padding()
    }
    
    private func didEndResizing(size: CGSize) {
        print("View resizing ended. Final size: \(size)")
    }
}

I want to create a solution that tells me when the user has finished resizing a view. Currently, I’m using a timer for this, but I’m not satisfied with my approach because it doesn’t feel like idiomatic SwiftUI coding. I’m looking for a more native way to detect the final size of the view at the end of resizing. As we change the window size our view size would change.

import SwiftUI

struct ContentView: View {
    
    @State private var resizeTimer: Timer? = nil
    
    var body: some View {
        GeometryReader { geometryValue in
            Color.white
                .onChange(of: geometryValue.size) { newValue in

                    // Invalidate any existing timer
                    resizeTimer?.invalidate()

                    // Start a new timer to detect when resizing ends
                    resizeTimer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: false) {  _ in // [weak self]
                        didEndResizing(size: newValue)
                    }
                }
        }
        .padding()
    }
    
    private func didEndResizing(size: CGSize) {
        print("View resizing ended. Final size: \(size)")
    }
}
Share Improve this question edited Nov 16, 2024 at 21:20 swiftPunk asked Nov 16, 2024 at 16:52 swiftPunkswiftPunk 1 4
  • How would the user resize the view? Views in general cannot be resized by the user unless you deliberately provide a mechanism for resizing it. Do you mean the window instead? – Sweeper Commented Nov 16, 2024 at 16:59
  • @swiftPunk How is a view supposed to know intrinsically when a size change is finished if the input for the change comes from external events like user inputs? Maybe I don't understand your question and you mean that you give a view a target size from the outside and the size of your view changes several times on the way to this target size? – ITGuy Commented Nov 16, 2024 at 19:12
  • @Sweeper: window, as we change the window size our view size would change. – swiftPunk Commented Nov 16, 2024 at 20:59
  • 1 @LeoDabus I disagree with the duplicate closure. The first reason is that the duplicate target is using AppKit and this question is about SwiftUI. While you can use the same approach as the answer in the duplicate target to solve this in SwiftUI, there might be other SwiftUI-specific ways (perhaps in the future). The second reason is that for a SwiftUI app with multiple windows, the didEndLiveResizeNotification approach also involves getting the current NSWindow, which is not trivial to do. – Sweeper Commented Nov 19, 2024 at 19:05
Add a comment  | 

1 Answer 1

Reset to default 1

To detect the end of a window resizing session, you can observe the NSWindow.didEndLiveResizeNotification.

.onReceive(NotificationCenter.default.publisher(for: NSWindow.didEndLiveResizeNotification)) { notification in
    print("Resize did end!")
}

notification.object tells you which NSWindow it is that got resized, so if your app has multiple windows, you can check that to respond only to resizes of the view's own window. For example:

struct ContentView: View {
    @State private var myWindow: NSWindow?
    var body: some View {
        Color.blue
            .background { WindowAccessor(window: $myWindow) }
            .onReceive(NotificationCenter.default.publisher(for: NSWindow.didEndLiveResizeNotification)) { notification in
                if (notification.object as? NSWindow) == myWindow {
                    print("Did resize!")
                }
            }
    }
}

struct WindowAccessor: NSViewRepresentable {
    @Binding var window: NSWindow?
    
    class MoveToWindowDetector: NSView {
        var onMoveToWindow: (NSWindow?) -> Void = { _ in }
        
        override func viewDidMoveToWindow() {
            onMoveToWindow(window)
        }
    }

    func makeNSView(context: Context) -> MoveToWindowDetector {
        MoveToWindowDetector()
    }

    func updateNSView(_ nsView: MoveToWindowDetector, context: Context) {
        nsView.onMoveToWindow = { window = $0 }
    }
}

WindowAccessor is slightly modified from Asperi's answer here.

本文标签: swiftHow can I detect when view resizing is finishedStack Overflow