在 SwiftUI 中,如何在执行手势时持续更新视图?
In SwiftUI, how do I continuously update a view while a gesture is being preformed?
这是自学如何在 iOS 中创建基本绘画应用程序(如 MSPaint)的持续尝试的一部分。我正在使用 SwiftUI 和 CoreImage 来执行此操作。
虽然我全神贯注于 CoreImage 中的像素操作(我一直在研究 this),但我不确定如何向 SwiftUI 添加拖动手势以便我可以“绘画” ".
使用拖动手势,我想这样做:
onBegin 和 onChanged:
- 将我手指的当前 x、y 位置发送到处理 CoreImage 操作的函数;
- 接收并显示更新后的图像;
- 重复直到手势结束。
换句话说,随着我手指的移动不断更新图像。
更新:我查看了下面 Asperi 的响应,并在 .onAppear 下面添加了 .gesture。但是,这会导致警告“在视图更新期间修改状态,这将导致未定义的行为。”
struct ContentView: View {
@State private var image: Image?
@GestureState var location = CGPoint(x: 0, y: 0)
var body: some View {
VStack {
image?
.resizable()
.scaledToFit()
}
.onAppear(perform: newPainting)
.gesture(
DragGesture()
.updating($location) { (value, gestureState, transaction) in
gestureState = value.location
paint(location: location)
}
)
}
func newPainting() {
guard let newPainting = createBlankCanvas(size: CGSize(width: 128, height: 128)) else {
print("failed to create a blank canvas")
return
}
image = Image(uiImage: newPainting)
}
func createBlankCanvas(size: CGSize, filledWithColor color: UIColor = UIColor.clear, scale: CGFloat = 0.0, opaque: Bool = false) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
color.set()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func paint(location: CGPoint) {
// do the CoreImage manipulation here
// now, take the output of the CI manipulation and
// attempt to get a CGImage from our CIImage
if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
// convert that to a UIImage
let uiImage = UIImage(cgImage: cgimg)
// and convert that to a SwiftUI image
image = Image(uiImage: uiImage) // <- Modifying state during view update, this will cause undefined behavior.
}
}
}
在哪里添加手势并让它重复调用 paint() 函数?
只要手势继续,如何让视图不断更新?
谢谢!
您不应将 SwiftUI 视图(如 Image
)存储在 @State
变量中。相反,您应该存储 UIImage
:
struct ContentView: View {
@State private var uiImage: UIImage?
@GestureState var location = CGPoint(x: 0, y: 0)
var body: some View {
VStack {
uiImage.map { uiImage in
Image(uiImage: uiImage)
.resizable()
.scaledToFit()
}
}
.onAppear(perform: newPainting)
.gesture(
DragGesture()
.updating($location) { (value, gestureState, transaction) in
gestureState = value.location
paint(location: location)
}
)
}
func newPainting() {
guard let newPainting = createBlankCanvas(size: CGSize(width: 128, height: 128)) else {
print("failed to create a blank canvas")
return
}
uiImage = newPainting
}
func createBlankCanvas(size: CGSize, filledWithColor color: UIColor = UIColor.clear, scale: CGFloat = 0.0, opaque: Bool = false) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
color.set()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func paint(location: CGPoint) {
// do the CoreImage manipulation here
// now, take the output of the CI manipulation and
// attempt to get a CGImage from our CIImage
if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
// convert that to a UIImage
uiImage = UIImage(cgImage: cgimg)
}
}
}
这是自学如何在 iOS 中创建基本绘画应用程序(如 MSPaint)的持续尝试的一部分。我正在使用 SwiftUI 和 CoreImage 来执行此操作。
虽然我全神贯注于 CoreImage 中的像素操作(我一直在研究 this),但我不确定如何向 SwiftUI 添加拖动手势以便我可以“绘画” ".
使用拖动手势,我想这样做:
onBegin 和 onChanged:
- 将我手指的当前 x、y 位置发送到处理 CoreImage 操作的函数;
- 接收并显示更新后的图像;
- 重复直到手势结束。
换句话说,随着我手指的移动不断更新图像。
更新:我查看了下面 Asperi 的响应,并在 .onAppear 下面添加了 .gesture。但是,这会导致警告“在视图更新期间修改状态,这将导致未定义的行为。”
struct ContentView: View {
@State private var image: Image?
@GestureState var location = CGPoint(x: 0, y: 0)
var body: some View {
VStack {
image?
.resizable()
.scaledToFit()
}
.onAppear(perform: newPainting)
.gesture(
DragGesture()
.updating($location) { (value, gestureState, transaction) in
gestureState = value.location
paint(location: location)
}
)
}
func newPainting() {
guard let newPainting = createBlankCanvas(size: CGSize(width: 128, height: 128)) else {
print("failed to create a blank canvas")
return
}
image = Image(uiImage: newPainting)
}
func createBlankCanvas(size: CGSize, filledWithColor color: UIColor = UIColor.clear, scale: CGFloat = 0.0, opaque: Bool = false) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
color.set()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func paint(location: CGPoint) {
// do the CoreImage manipulation here
// now, take the output of the CI manipulation and
// attempt to get a CGImage from our CIImage
if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
// convert that to a UIImage
let uiImage = UIImage(cgImage: cgimg)
// and convert that to a SwiftUI image
image = Image(uiImage: uiImage) // <- Modifying state during view update, this will cause undefined behavior.
}
}
}
在哪里添加手势并让它重复调用 paint() 函数? 只要手势继续,如何让视图不断更新?
谢谢!
您不应将 SwiftUI 视图(如 Image
)存储在 @State
变量中。相反,您应该存储 UIImage
:
struct ContentView: View {
@State private var uiImage: UIImage?
@GestureState var location = CGPoint(x: 0, y: 0)
var body: some View {
VStack {
uiImage.map { uiImage in
Image(uiImage: uiImage)
.resizable()
.scaledToFit()
}
}
.onAppear(perform: newPainting)
.gesture(
DragGesture()
.updating($location) { (value, gestureState, transaction) in
gestureState = value.location
paint(location: location)
}
)
}
func newPainting() {
guard let newPainting = createBlankCanvas(size: CGSize(width: 128, height: 128)) else {
print("failed to create a blank canvas")
return
}
uiImage = newPainting
}
func createBlankCanvas(size: CGSize, filledWithColor color: UIColor = UIColor.clear, scale: CGFloat = 0.0, opaque: Bool = false) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
color.set()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func paint(location: CGPoint) {
// do the CoreImage manipulation here
// now, take the output of the CI manipulation and
// attempt to get a CGImage from our CIImage
if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
// convert that to a UIImage
uiImage = UIImage(cgImage: cgimg)
}
}
}