我正在使用 ReactiveCocoa 构建一个应用程序。顶 View 是一个菜单,可以向下拉然后向上推。我必须使用两种不同的手势识别器——一种用于下拉,一种用于向上推。一次只能启用一个——这是我的问题。状态。
我正在使用 BlocksKit 扩展来设置手势识别器。
self.panHeaderDownGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithHandler:^(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) {
UIPanGestureRecognizer *recognizer = (UIPanGestureRecognizer *)sender;
CGPoint translation = [recognizer translationInView:self.view];
if (state == UIGestureRecognizerStateChanged)
{
[self.downwardHeaderPanSubject sendNext:@(translation.y)];
}
else if (state == UIGestureRecognizerStateEnded)
{
// Determine the direction the finger is moving and ensure if it was moving down, that it exceeds the minimum threshold for opening the menu.
BOOL movingDown = ([recognizer velocityInView:self.view].y > 0 && translation.y > kMoveDownThreshold);
// Animate the change
[UIView animateWithDuration:0.25f animations:^{
if (movingDown)
{
[self.downwardHeaderPanSubject sendNext:@(kMaximumHeaderTranslationThreshold)];
}
else
{
[self.downwardHeaderPanSubject sendNext:@(0)];
}
} completion:^(BOOL finished) {
[self.menuFinishedTransitionSubject sendNext:@(movingDown)];
}];
}
}];
在我的 initWithNibName:bundle:
方法中,我正在设置以下 RACSubject
。
self.headerMovementSubject = [RACSubject subject];
[self.headerMovementSubject subscribeNext:^(id x) {
@strongify(self);
// This is the ratio of the movement. 0 is closed and 1 is open.
// Values less than zero are treated as zero.
// Values greater than one are valid and will be extrapolated beyond the fully open menu.
CGFloat ratio = [x floatValue];
CGRect headerFrame = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), kHeaderHeight + ratio * kMaximumHeaderTranslationThreshold);
if (ratio < 0)
{
headerFrame = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), kHeaderHeight);
}
self.headerViewController.view.frame = headerFrame;
}];
// This subject is responsible for receiving translations from a gesture recognizers and turning
// thos values into ratios. These ratios are fead into other signals.
self.downwardHeaderPanSubject = [RACSubject subject];
[self.downwardHeaderPanSubject subscribeNext:^(NSNumber *translation) {
@strongify(self);
CGFloat verticalTranslation = [translation floatValue];
CGFloat effectiveRatio = 0.0f;
if (verticalTranslation <= 0)
{
effectiveRatio = 0.0f;
}
else if (verticalTranslation <= kMaximumHeaderTranslationThreshold)
{
effectiveRatio = fabsf(verticalTranslation / kMaximumHeaderTranslationThreshold);
}
else
{
CGFloat overshoot = verticalTranslation - kMaximumHeaderTranslationThreshold;
CGFloat y = 2 * sqrtf(overshoot + 1) - 2;
effectiveRatio = 1.0f + (y / kMaximumHeaderTranslationThreshold);
}
[self.headerMovementSubject sendNext:@(effectiveRatio)];
}];
// This subject is responsible for mapping this value to other signals and state (ugh).
self.menuFinishedTransitionSubject = [RACReplaySubject subject];
[self.menuFinishedTransitionSubject subscribeNext:^(NSNumber *menuIsOpenNumber) {
@strongify(self);
BOOL menuIsOpen = menuIsOpenNumber.boolValue;
self.panHeaderDownGestureRecognizer.enabled = !menuIsOpen;
self.panHeaderUpGestureRecognizer.enabled = menuIsOpen;
self.otherViewController.view.userInteractionEnabled = !menuIsOpen;
if (menuIsOpen)
{
[self.headerViewController flashScrollBars];
}
}];
这里发生了很多事情。由于我的主题数量几乎是我在此处列出的两倍(也有用于平移手势识别器的),加上另一组识别器与页脚的类似交互。这是很多科目。
我的问题分为两部分:
- 有没有更好的方法来设置我想要的那种链接?我也在我的俯卧撑姿势中重新使用了一些主题,看起来非常相似。我有很多
RACSubjects
,但看起来很简陋。 menuFinishedTransitionSubject
主要用于管理手势识别器的状态。我尝试绑定(bind)他们的enabled
属性,但没有任何运气。这里有什么建议吗?
最佳答案
让我们关注显式订阅,因为它们通常是重写命令式代码的容易获得的成果。
首先,根据显示的代码,看起来 headerMovementSubject
只是从 downwardHeaderPanSubject
中获取值(而不是其他任何地方)。这是一个很容易写成转换的候选者:
RACSignal *headerFrameSignal = [[self.downwardHeaderPanSubject
map:^(NSNumber *translation) {
CGFloat verticalTranslation = [translation floatValue];
CGFloat effectiveRatio = 0.0f;
// Calculate effectiveRatio.
return @(effectiveRatio);
}]
map:^(NSNumber *effectiveRatio) {
// Calculate headerFrame.
return @(headerFrame);
}];
然后,我们可以使用绑定(bind)来代替操作 self.headerViewController.view.frame
的副作用:
RAC(self.headerViewController.view.frame) = headerFrameSignal;
我们可以用 menuFinishedTransitionSubject
中的 bool 值做类似的事情:
RAC(self.panHeaderDownGestureRecognizer.enabled) = [self.menuFinishedTransitionSubject not];
RAC(self.panHeaderUpGestureRecognizer.enabled) = self.menuFinishedTransitionSubject;
RAC(self.otherViewController.view.userInteractionEnabled) = [self.menuFinishedTransitionSubject not];
不幸的是,-flashScrollBars
仍然需要作为副作用调用,但我们至少可以将过滤从 block 中移除:
[[self.menuFinishedTransitionSubject
filter:^(NSNumber *menuIsOpen) {
return menuIsOpen.boolValue;
}]
subscribeNext:^(id _) {
@strongify(self);
[self.headerViewController flashScrollBars];
}];
如果你真的想花哨的话,很多手势识别器逻辑可以用流转换来表示,动画可以用 ReactiveCocoaLayout 来实现。 ,但这是对它自己的重写。
关于ios - 如何将 ReactiveCocoa 与手势识别器一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16320429/