gpt4 book ai didi

ios - 使用 ReactiveCocoa 跟踪远程对象的 UI 更新

转载 作者:可可西里 更新时间:2023-11-01 03:52:10 26 4
gpt4 key购买 nike

我正在制作一个 iOS 应用程序,它可以让您远程控制在桌面上播放的应用程序中的音乐。

最困难的问题之一是能够正确更新“跟踪器”的位置(显示当前播放歌曲的时间位置和持续时间)。这里有几个输入源:

  • 启动时, Remote 发送网络请求以获取当前播放歌曲的初始位置和时长。
  • 当用户使用 Remote 调整跟踪器的位置时,它会向音乐应用程序发送网络请求以更改歌曲的位置。
  • 如果用户使用桌面上的应用程序更改跟踪器的位置,该应用程序会向 Remote 发送一个网络请求,其中包含跟踪器的新位置。
  • 如果当前正在播放歌曲,则跟踪器的位置每 0.5 秒左右更新一次。

目前,跟踪器是一个由“播放器”模型支持的 UISlider。每当用户更改 slider 上的位置时,它都会更新模型并发送网络请求,如下所示:

在 NowPlayingViewController.m 中

[[slider rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UISlider *x) {
[playerModel seekToPosition:x.value];
}];

[RACObserve(playerModel, position) subscribeNext:^(id x) {
slider.value = player.position;
}];

在 PlayerModel.m 中:

@property (nonatomic) NSTimeInterval position;

- (void)seekToPosition:(NSTimeInterval)position
{
self.position = position;
[self.client newRequestWithMethod:@"seekTo" params:@[positionArg] callback:NULL];
}

- (void)receivedPlayerUpdate:(NSDictionary *)json
{
self.position = [json objectForKey:@"position"]
}

问题是当用户“摆弄” slider 时,将许多网络请求排队,这些请求都在不同的时间返回。当收到响应时,用户可以再次移动 slider ,将 slider 移回之前的值。

我的问题:在此示例中如何正确使用 ReactiveCocoa,确保处理来自网络的更新,但前提是用户此后没有移动 slider ?

最佳答案

your GitHub thread about this你说你想将 Remote 的更新视为规范的。这很好,因为(正如 Josh Abernathy 在那里建议的那样),无论是否使用 RAC,您都需要选择两个来源之一来优先考虑(或者您需要时间戳,但随后您需要一个引用时钟......)。

鉴于您的代码并忽略 RAC,解决方案只是在 seekToPosition: 中设置一个标志并使用计时器取消设置。检查 recievedPlayerUpdate: 中的标志,如果已设置则忽略更新。

顺便说一下,您应该使用 RAC() 宏来绑定(bind)您的 slider 值,而不是您拥有的 subscribeNext::

RAC(slider, value) = RACObserve(playerModel, position);

不过,您绝对可以构建一个信号链来执行您想要的操作。您有四个信号需要组合。

最后一项,定期更新,可以用interval:onScheduler: :

[[RACSignal interval:kPositionFetchSeconds
onScheduler:[RACScheduler scheduler]] map:^(id _){
return /* Request position over network */;
}];

map: 只是忽略 interval:... 信号产生的日期,并获取位置。由于您的请求和来自桌面的消息具有同等优先级,merge:那些在一起:

[RACSignal merge:@[desktopPositionSignal, timedRequestSignal]];

不过,您决定在用户​​触摸 slider 时不希望这些信号中的任何一个通过。这可以通过两种方式之一来完成。使用我建议的标志,你可以 filter:合并后的信号:

[mergedSignal filter:^BOOL (id _){ return userFiddlingWithSlider; }];

比这更好的——避免额外的状态——是用 throttle: 的组合构建一个操作和 sample:在另一个信号未发送任何内容后,以一定间隔传递来自信号的值:

[mergedSignal sample:
[sliderSignal throttle:kUserFiddlingWithSliderInterval]];

(当然,您可能希望在合并之前以相同的方式限制/采样 interval:onScheduler: 信号,以避免不必要的网络请求。)

您可以将所有这些放在 PlayerModel 中,将其绑定(bind)到 position。您只需为 PlayerModel 提供 slider 的 rac_signalForControlEvents:,然后合并 slider 值。由于您在一条链中的多个位置使用相同的信号,我相信您想要 "multicast"它。

最后,使用startWith:将上面的第一个项目(桌面应用程序的初始位置)放入流中。

RAC(self, position) = 
[[RACSignal merge:@[sampledSignal,
[sliderSignal map:^id(UISlider * slider){
return [slider value];
}]]
] startWith:/* Request position over network */];

我将把每个信号分解成自己的变量或将它们串在一起的 Lisp 风格的决定留给你。

顺便说一句,我发现在处理此类问题时实际绘制出信号链很有帮助。我 made a quick diagram for your scenario .它有助于将信号本身视为实体,而不是担心它们携带的值。

关于ios - 使用 ReactiveCocoa 跟踪远程对象的 UI 更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20570769/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com