SwiftUI:在最小值和最大值之间更改值的放大手势
SwiftUI: magnification gesture that changes value between min and max
我想用滑块做同样的事情:
@State itemSize: CGFloat = 55.60
....
Text("ItemSize: \(itemSize)")
Slider(value: $itemSize, in: 45...120)
但有放大手势
我为此创建了修改器:
import SwiftUI
@available(OSX 11.0, *)
public extension View {
func magnificationZoomer(value: Binding<CGFloat>,min: CGFloat, max: CGFloat) -> some View
{
self.modifier( MagnificationZoomerMod(minZoom: min, maxZoom: max, zoom: value) )
}
}
@available(OSX 11.0, *)
public struct MagnificationZoomerMod: ViewModifier {
let minZoom: CGFloat
let maxZoom: CGFloat
@Binding var zoom: CGFloat
public func body (content: Content) -> some View
{
content
.gesture(
MagnificationGesture()
.onChanged() { val in
let magnification = (val - 1) * 2.2
print("magnification = \(magnification)")
zoom = max(min(zoom + magnification, maxZoom), minZoom)
}
)
}
}
使用示例:
@State itemSize: CGFloat = 55.60
var body: some View {
HStack {
VStack {
Text("ItemSize: \(itemSize)")
Slider(value: $itemSize, in: 45...120)
}
}
.frame(width: 500, height: 500)
.magnificationZoomer(value: $itemSize, min: 45, max: 120)
}
但是我在使用这段代码时遇到了一些问题:
- 它以一种奇怪的方式滑动值 -- 有时以正确的速度,它不会改变它
- 使用一段时间后它完全停止工作
我做错了什么?
它就像魔术一样工作,您可以通过 Slider 或您的手指在 MagnificationGesture 上更改圆的 size/scale , 共同努力, 共同沉沦
import SwiftUI
struct ContentView: View {
let minZoom: CGFloat = 0.5
let maxZoom: CGFloat = 1.5
@State private var scale: CGFloat = 1.0
@State private var lastScale: CGFloat = 1.0
@State private var magnitudeIsActive: Bool = Bool()
var body: some View {
ZStack {
Circle()
.fill(Color.red)
.scaleEffect(scale)
.gesture(magnificationGesture.simultaneously(with: dragGesture)) // <<: Here: adding unneeded dragGesture! on macOS! no need on iOS!
VStack {
Spacer()
Text("scale: " + scale.rounded)
HStack {
Button("min") { scale = minZoom; lastScale = scale }
Slider(value: Binding.init(get: { () -> CGFloat in return scale },
set: { (newValue) in if !magnitudeIsActive { scale = newValue; lastScale = newValue } }), in: minZoom...maxZoom)
Button("max") { scale = maxZoom; lastScale = scale }
}
}
}
.padding()
.compositingGroup()
.shadow(radius: 10.0)
.animation(Animation.easeInOut(duration: 0.2), value: scale)
}
var magnificationGesture: some Gesture {
MagnificationGesture(minimumScaleDelta: 0.0)
.onChanged { value in
if !magnitudeIsActive { magnitudeIsActive = true }
let magnification = (lastScale + value.magnitude - 1.0)
if (magnification >= minZoom && magnification <= maxZoom) {
scale = magnification
}
else if (magnification < minZoom) {
scale = minZoom
}
else if (magnification > maxZoom) {
scale = maxZoom
}
}
.onEnded { value in
let magnification = (lastScale + value.magnitude - 1.0)
if (magnification >= minZoom && magnification <= maxZoom) {
lastScale = magnification
}
else if (magnification < minZoom) {
lastScale = minZoom
}
else if (magnification > maxZoom) {
lastScale = maxZoom
}
scale = lastScale
magnitudeIsActive = false
}
}
var dragGesture: some Gesture { DragGesture(minimumDistance: 0.0) } // <<: Here: this Extra un-needed gesture would keep magnificationGesture alive! And Stop it to get killed in macOS! We do not need this line of code in iOS!
}
extension CGFloat { var rounded: String { get { return String(Double(self*100.0).rounded()/100.0) } } }
我想用滑块做同样的事情:
@State itemSize: CGFloat = 55.60
....
Text("ItemSize: \(itemSize)")
Slider(value: $itemSize, in: 45...120)
但有放大手势
我为此创建了修改器:
import SwiftUI
@available(OSX 11.0, *)
public extension View {
func magnificationZoomer(value: Binding<CGFloat>,min: CGFloat, max: CGFloat) -> some View
{
self.modifier( MagnificationZoomerMod(minZoom: min, maxZoom: max, zoom: value) )
}
}
@available(OSX 11.0, *)
public struct MagnificationZoomerMod: ViewModifier {
let minZoom: CGFloat
let maxZoom: CGFloat
@Binding var zoom: CGFloat
public func body (content: Content) -> some View
{
content
.gesture(
MagnificationGesture()
.onChanged() { val in
let magnification = (val - 1) * 2.2
print("magnification = \(magnification)")
zoom = max(min(zoom + magnification, maxZoom), minZoom)
}
)
}
}
使用示例:
@State itemSize: CGFloat = 55.60
var body: some View {
HStack {
VStack {
Text("ItemSize: \(itemSize)")
Slider(value: $itemSize, in: 45...120)
}
}
.frame(width: 500, height: 500)
.magnificationZoomer(value: $itemSize, min: 45, max: 120)
}
但是我在使用这段代码时遇到了一些问题:
- 它以一种奇怪的方式滑动值 -- 有时以正确的速度,它不会改变它
- 使用一段时间后它完全停止工作
我做错了什么?
它就像魔术一样工作,您可以通过 Slider 或您的手指在 MagnificationGesture 上更改圆的 size/scale , 共同努力, 共同沉沦
import SwiftUI
struct ContentView: View {
let minZoom: CGFloat = 0.5
let maxZoom: CGFloat = 1.5
@State private var scale: CGFloat = 1.0
@State private var lastScale: CGFloat = 1.0
@State private var magnitudeIsActive: Bool = Bool()
var body: some View {
ZStack {
Circle()
.fill(Color.red)
.scaleEffect(scale)
.gesture(magnificationGesture.simultaneously(with: dragGesture)) // <<: Here: adding unneeded dragGesture! on macOS! no need on iOS!
VStack {
Spacer()
Text("scale: " + scale.rounded)
HStack {
Button("min") { scale = minZoom; lastScale = scale }
Slider(value: Binding.init(get: { () -> CGFloat in return scale },
set: { (newValue) in if !magnitudeIsActive { scale = newValue; lastScale = newValue } }), in: minZoom...maxZoom)
Button("max") { scale = maxZoom; lastScale = scale }
}
}
}
.padding()
.compositingGroup()
.shadow(radius: 10.0)
.animation(Animation.easeInOut(duration: 0.2), value: scale)
}
var magnificationGesture: some Gesture {
MagnificationGesture(minimumScaleDelta: 0.0)
.onChanged { value in
if !magnitudeIsActive { magnitudeIsActive = true }
let magnification = (lastScale + value.magnitude - 1.0)
if (magnification >= minZoom && magnification <= maxZoom) {
scale = magnification
}
else if (magnification < minZoom) {
scale = minZoom
}
else if (magnification > maxZoom) {
scale = maxZoom
}
}
.onEnded { value in
let magnification = (lastScale + value.magnitude - 1.0)
if (magnification >= minZoom && magnification <= maxZoom) {
lastScale = magnification
}
else if (magnification < minZoom) {
lastScale = minZoom
}
else if (magnification > maxZoom) {
lastScale = maxZoom
}
scale = lastScale
magnitudeIsActive = false
}
}
var dragGesture: some Gesture { DragGesture(minimumDistance: 0.0) } // <<: Here: this Extra un-needed gesture would keep magnificationGesture alive! And Stop it to get killed in macOS! We do not need this line of code in iOS!
}
extension CGFloat { var rounded: String { get { return String(Double(self*100.0).rounded()/100.0) } } }