这是针对MacOS的。我试图弄清楚如何在用户按住自定义按钮的同时将值泵送到模型中。基本上,我试图重新创建一个MouseDown / MouseUp组合,并在两者之间触发一个计时器。仅使用LongPressGesture似乎不可能,所以我一直在尝试使用Combine和Timer,但仅获得部分成功。我有以下内容:
import SwiftUI
import Combine
var cancellable: Cancellable?
struct ContentView: View {
var body: some View {
ZStack{
FastForwardButton().frame(width: 40, height: 40)
}.frame(width: 200, height: 200)
}
}
struct FastForwardButton: View {
var timer = Timer.publish(every: 0.2, on: .main, in: .common)
@GestureState private var isPressed = false
// @State var cancellable: Cancellable?
var body: some View {
Rectangle()
.gesture(
LongPressGesture(minimumDuration: 4)
.updating($isPressed, body: { (currentState, state, transaction) in
if self.isPressed == false{
state = currentState
print("Timer Started")
cancellable = self.timer.connect()
}
})
)
.onReceive(timer) { time in
// Do Something here eg myModel.pump()
print("The time is \(time)")
if self.isPressed == false{
print("Timer Cancelled")
cancellable?.cancel()
// cancellable = nil
}
}
}
}
上面的方法一次。我得到:
“计时器已开始”
“时间是xxx”
.....
“时间是xxx”
“计时器已取消”
第二次按下我就得到了:
“计时器已开始”
没有进一步的动作
请注意,当我收到有关在更新时修改View的警告时,我还不得不将引用暂时移到View之外的cancellable。
谁能弄清楚为什么.onReceive关闭仅被调用一次?谢谢!
答案 0 :(得分:0)
尝试此(复制-粘贴-运行)macOS
import SwiftUI
import Combine
class Model: ObservableObject {
var timerPublisher: Timer.TimerPublisher?
var handle: AnyCancellable?
var startstop: Cancellable?
func start() {
timerPublisher = Timer.publish(every: 0.5, on: RunLoop.main, in: .default)
handle = timerPublisher?.sink(receiveValue: {
print($0)
})
startstop = timerPublisher?.connect()
print("start")
}
func stop() {
startstop?.cancel()
print("stop")
}
}
struct ContentView: View {
@ObservedObject var model = Model()
var body: some View {
VStack {
Button(action: {
self.model.start()
}) {
Text("Start")
}
Button(action: {
self.model.stop()
}) {
Text("Stop")
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
它显示了两个按钮,您可以开始停止发布...(Apsperi正确地是,一旦取消发布者,它就无法再次使用。这对于任何发布者都是如此,而不是特定于Timer.Publisher。
如果您想“发布”模型中的某些更改,则可以采用标准方式进行
func start() {
timerPublisher = Timer.publish(every: 0.5, on: RunLoop.main, in: .default)
handle = timerPublisher?.sink(receiveValue: {
print($0)
self.objectWillChange.send()
})
startstop = timerPublisher?.connect()
print("start")
}
问题是为什么不简单使用.autoconnect()?答案是灵活性,请考虑一下此模型
class Model: ObservableObject {
var timerPublisher: Timer.TimerPublisher?
var handle: AnyCancellable?
var startstop: Cancellable?
func createTimerPublisher() {
timerPublisher = Timer.publish(every: 0.5, on: RunLoop.main, in: .default)
handle = timerPublisher?.sink(receiveValue: {
print($0)
self.objectWillChange.send()
})
}
init() {
createTimerPublisher()
}
func start() {
startstop = timerPublisher?.connect()
print("start")
}
func stop() {
startstop?.cancel()
print("stop")
// or create it conditionaly for later use
createTimerPublisher()
}
}
更新,其中在某些视图上上下移动鼠标可启动/停止计时器
import SwiftUI
import Combine
class Model: ObservableObject {
var timerPublisher: Timer.TimerPublisher?
var handle: AnyCancellable?
var startstop: Cancellable?
func createTimerPublisher() {
timerPublisher = Timer.publish(every: 0.5, on: RunLoop.main, in: .default)
handle = timerPublisher?.sink(receiveValue: {
print($0)
self.objectWillChange.send()
})
}
init() {
createTimerPublisher()
}
func start() {
// if
startstop = timerPublisher?.connect()
print("start")
}
func stop() {
startstop?.cancel()
startstop = nil
print("stop")
// or create it conditionaly for later use
createTimerPublisher()
}
}
struct ContentView: View {
@ObservedObject var model = Model()
var body: some View {
VStack {
Button(action: {
self.model.start()
}) {
Text("Start")
}
Button(action: {
self.model.stop()
}) {
Text("Stop")
}
MouseUpDownRepresentable(content: Rectangle()).environmentObject(model)
.frame(width: 50, height: 50)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
class MouseUpDownViewClass<Content>: NSHostingView<Content> where Content : View {
let model: Model
required init(model: Model, rootView: Content) {
self.model = model
super.init(rootView: rootView)
}
@objc required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
required init(rootView: Content) {
fatalError("init(rootView:) has not been implemented")
}
override func mouseUp(with event: NSEvent) {
print("mouse up")
model.stop()
}
override func mouseDown(with event: NSEvent) {
print("mouse down")
model.start()
}
}
struct MouseUpDownRepresentable<Content>: NSViewRepresentable where Content: View {
@EnvironmentObject var model: Model
let content: Content
func makeNSView(context: Context) -> NSHostingView<Content> {
return MouseUpDownViewClass(model: model, rootView: self.content)
}
func updateNSView(_ nsView: NSHostingView<Content>, context: Context) {
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
如果它看起来太复杂,则可以仅用SwiftUI手势创建它。技巧使用了不同的手势识别
struct TouchView: View {
@State var pressed = false
var body: some View {
Circle().fill(pressed ? Color.yellow : Color.orange)
.gesture(
DragGesture(minimumDistance: 0)
.onChanged({ (touch) in
if self.pressed == false {
self.pressed = true
print("start")
}
})
.onEnded({ (touch) in
print("stop")
self.pressed = false
})
)
}
}