如何在 ReactiveCocoa 4 中创建自定义信号?
How to create custom signal in ReactiveCocoa 4?
我有以下设置,一个 GridView
由 GridViewCell
组成。
网格视图
class GridView : UIView {
var gridViewCells: [GridViewCell] = []
let tapHandler: Position -> ()
init(frame: CGRect, tapHandler: Position -> ()) {
self.tapHandler = tapHandler
super.init(frame: frame)
self.gridViewCells = createCells(self)
addCellsToView(self.gridViewCells)
}
func addCellsToView(cells: [GridViewCell]) {
for cell in cells {
self.addSubview(cell)
}
}
}
GridViewCell
class GridViewCell: UIImageView {
let position: Position
let tapHandler: Position -> ()
init(frame: CGRect, position: Position, tapHandler: Position -> ()) {
self.position = position
self.tapHandler = tapHandler
super.init(frame: frame)
}
func handleTap(sender: UITapGestureRecognizer) {
self.tapHandler(self.position)
}
}
请注意,我省略了一些不太相关的代码部分,只要知道在 createCells()
中创建单元格时,每个单元格都会得到一个 UITapGestureRecognizer
目标 handleTap:
,我是还使用:
typealias Position = (Int, Int)
因此,如您所见,每当 GridView
被实例化时,它都会传递一个回调函数 tapHandler: Position -> ()
,当用户点击一个单元格时,该函数最终会被该单元格调用。
现在,我想将点击事件变成 RAC Signal
。我不知道如何处理这个问题,因为我是 RAC 的新手。感谢 Colin Eberhardts blog articles,我设法对构建块有了基本的了解并实现了一个自定义信号,如下所示:
func createSignal() -> Signal<String, NoError> {
var count = 0
return Signal {
sink in
NSTimer.schedule(repeatInterval: 1.0) { timer in
sink.sendNext("tick #\(count++)")
}
return nil
}
}
我现在基本上想要一个类似的行为,只是发出的事件不是由 NSTimer
触发,而是由 handleTap()
函数触发。
您可以使用 Signal.pipe()
完成此操作。这为您提供了一个元组,其中 signal
和 observer
都与该信号相关联:
let (signal, observer) = Signal<String, NoError>.pipe()
您可以像在示例中使用 sink
一样使用它(请注意 sink
只是 observer
的旧术语 :))
但是对于按钮或手势识别器,您可以利用 RAC 2
扩展。例如:
let signal: SignalProducer<(), NoError> = gestureRecognizer
.rac_gestureSignal()
.toSignalProducer()
.mapError { fatalError("Unexpected error: \(error)"); return () } // errors cannot occur, but because they weren't typed in `RACSignal` we have to explicitly ignore them.
.map { _ in () }
或UIControl.rac_signalForControlEvents
.
我发布了 a gist
扩展,以简化其中一些常见操作。希望有用!
我有以下设置,一个 GridView
由 GridViewCell
组成。
网格视图
class GridView : UIView {
var gridViewCells: [GridViewCell] = []
let tapHandler: Position -> ()
init(frame: CGRect, tapHandler: Position -> ()) {
self.tapHandler = tapHandler
super.init(frame: frame)
self.gridViewCells = createCells(self)
addCellsToView(self.gridViewCells)
}
func addCellsToView(cells: [GridViewCell]) {
for cell in cells {
self.addSubview(cell)
}
}
}
GridViewCell
class GridViewCell: UIImageView {
let position: Position
let tapHandler: Position -> ()
init(frame: CGRect, position: Position, tapHandler: Position -> ()) {
self.position = position
self.tapHandler = tapHandler
super.init(frame: frame)
}
func handleTap(sender: UITapGestureRecognizer) {
self.tapHandler(self.position)
}
}
请注意,我省略了一些不太相关的代码部分,只要知道在 createCells()
中创建单元格时,每个单元格都会得到一个 UITapGestureRecognizer
目标 handleTap:
,我是还使用:
typealias Position = (Int, Int)
因此,如您所见,每当 GridView
被实例化时,它都会传递一个回调函数 tapHandler: Position -> ()
,当用户点击一个单元格时,该函数最终会被该单元格调用。
现在,我想将点击事件变成 RAC Signal
。我不知道如何处理这个问题,因为我是 RAC 的新手。感谢 Colin Eberhardts blog articles,我设法对构建块有了基本的了解并实现了一个自定义信号,如下所示:
func createSignal() -> Signal<String, NoError> {
var count = 0
return Signal {
sink in
NSTimer.schedule(repeatInterval: 1.0) { timer in
sink.sendNext("tick #\(count++)")
}
return nil
}
}
我现在基本上想要一个类似的行为,只是发出的事件不是由 NSTimer
触发,而是由 handleTap()
函数触发。
您可以使用 Signal.pipe()
完成此操作。这为您提供了一个元组,其中 signal
和 observer
都与该信号相关联:
let (signal, observer) = Signal<String, NoError>.pipe()
您可以像在示例中使用 sink
一样使用它(请注意 sink
只是 observer
的旧术语 :))
但是对于按钮或手势识别器,您可以利用 RAC 2
扩展。例如:
let signal: SignalProducer<(), NoError> = gestureRecognizer
.rac_gestureSignal()
.toSignalProducer()
.mapError { fatalError("Unexpected error: \(error)"); return () } // errors cannot occur, but because they weren't typed in `RACSignal` we have to explicitly ignore them.
.map { _ in () }
或UIControl.rac_signalForControlEvents
.
我发布了 a gist
扩展,以简化其中一些常见操作。希望有用!