Поверните представление, чтобы всегда смотреть на указатель мыши в SwiftUI - PullRequest
0 голосов
/ 27 мая 2020

Фон: я постоянно пытаюсь повернуть изображение так, чтобы оно смотрело на мышь.

Подробности проблемы: Перемещение вида, основанное исключительно на движении мыши влево или вправо, вверх или вниз, работает нормально (см. Версию 1). Но одновременное использование обоих вариантов (нелинейное или ar c движение с помощью мыши) не работает, как только мышь движется вниз (когда y становится отрицательным) и вращается (когда x становится отрицательным) и положение просмотр регрессов.

Для этого я пытаюсь динамически переключать значения поворота с отрицательного на положительный в зависимости от того, на какой стороне экрана находится мышь (см. Версию 2). Но это похоже на плохую реализацию и довольно глючную.

Вопрос: Как мне лучше это сделать?

Я основываю свой код выборки курсора на здесь .

Версия 1-

Проблема: перемещение вида, основанное исключительно на движении мыши влево или вправо, вверх или вниз, работает нормально, но эта проблема возникает при попытке переместить мышь нелинейным образом:

Рисование

import SwiftUI

struct ContentView: View {
    var window: NSWindow! = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
        styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
        backing: .buffered, defer: false)
    var mouseLocation: NSPoint { NSEvent.mouseLocation }
    var location: NSPoint { window.mouseLocationOutsideOfEventStream }
    @State var position: NSPoint = NSPoint.init()
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Rectangle()
                    .fill(Color.red)
                    .frame(width: 200, height: 200)
                    .rotationEffect(
                        (self.position.y > 60) ?
                        .degrees(Double(self.position.x) / 2)
                        : .degrees(Double(self.position.x) / 2)
                    )
                    .rotationEffect(
                        (self.position.x > 320) ?
                        .degrees(Double(self.position.y * -1))
                            : .degrees(Double(self.position.y) / 2)
                    )

            }.frame(width: geometry.size.width, height: geometry.size.height)
                .onAppear() {
                    //Setup
                    self.window.center();
                    self.window.setFrameAutosaveName("Main Window")

                    /* Get mouse location on movement*/
                    NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) {
                        self.position = self.location //Save location
                        print("mouse location:", Double(self.position.x),  Double(self.position.y))
                        return $0
                    }
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

Версия 2 - вроде работает но глючит:

Рисование

import SwiftUI

struct ContentView: View {
    var window: NSWindow! = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
        styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
        backing: .buffered, defer: false)
    var mouseLocation: NSPoint { NSEvent.mouseLocation }
    var location: NSPoint { window.mouseLocationOutsideOfEventStream }
    @State var position: NSPoint = NSPoint.init()
    var body: some View {
        GeometryReader { geometry in
            VStack {

                Rectangle()
                    .fill(Color.red)
                    .frame(width: 200, height: 200)
                    .rotationEffect(
                        (self.position.x > 181) ?
                        .degrees(Double(self.position.x) / 2)
                        : .degrees(Double(self.position.x * -1) / 2)
                    )
                    .rotationEffect(
                        (self.position.x > 181) ?
                        .degrees(Double(self.position.y * -1) / 2)
                        : .degrees(Double(self.position.y) / 2)
                    )

            }.frame(width: geometry.size.width, height: geometry.size.height)
                .onAppear() {
                    //Setup
                    self.window.center();
                    self.window.setFrameAutosaveName("Main Window")

                    /* Get mouse location on movement*/
                    NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) {
                        self.position = self.location //Save location
                        print("mouse location:", Double(self.position.x),  Double(self.position.y))
                        return $0
                    }
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

1 Ответ

1 голос
/ 27 мая 2020

проверьте это:

несколько замечаний:

1) я изменил его на ios (должно быть легко вернуться к macos, потому что разница только в получении позиции

2) я сделал градиент прямоугольника, чтобы было ясно, в каком направлении вращается прямоугольник

3) я понятия не имею, что вы рассчитываете, но, на мой взгляд, вы можете только сделать это правильно с тригонометрией c функции

struct ContentView: View {

    @State var position : CGPoint = .zero
    @State var offset : CGSize = .zero
    @State var translation : CGSize = .zero

    @State var angle : Double = 0

    func calcAngle(_ geometry: GeometryProxy) -> Angle {

        let rectPosition = CGPoint(x: geometry.size.width / 2, y: geometry.size.height / 2)
        // tan alpha = dx / dy
        var alpha : Double

        if rectPosition.y == position.y {
            alpha = 0
        } else {
            var dx = (position.x - rectPosition.x)
            let dy = (position.y - rectPosition.y)

            if dy > 0 {
                dx = -dx
            }

            let r = sqrt(abs(dx * dx) + abs(dy * dy))
            alpha = Double(asin(dx / r))

            if dy > 0 {
                alpha = alpha + Double.pi
            }
        }
        return Angle(radians: alpha)
    }

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                Rectangle()
                    .fill(LinearGradient(gradient: Gradient(colors: [Color.red, Color.yellow]), startPoint: UnitPoint.top, endPoint: UnitPoint.bottom))
                    .frame(width: 200, height: 200)
                    .position(CGPoint(x: geometry.size.width / 2, y: geometry.size.height / 2))
                    .rotationEffect(self.calcAngle(geometry), anchor: UnitPoint(x: 0.5, y: 0.5))

                Circle().fill(Color.blue).frame(width:20,height:20).position(self.position)
                Circle().fill(Color.green).frame(width:20,height:20).position(self.position)

            }.frame(width: geometry.size.width, height: geometry.size.height)
            .gesture(
                DragGesture()
                    .onChanged { gesture in
                        self.position = gesture.location
                        self.translation = gesture.translation
                    }
                    .onEnded { _ in

                    }
            )
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

Result

...