Просматривая моей реализации, кажется, следующие условия:
- У меня есть подкласс
UIViewController
, который контроллер представления
- У меня есть подкласс из
UIView
, который является наложением (и впредь с именем «наложение») (на самом деле для меня это UIScrollView
, потому что он тоже должен идти вбок, но я попробую и отфильтровать ненужный код)
- У меня есть еще один подкласс
UIView
, который загружает содержимое наложения («содержание обертки»)
- Обертка содержания имеет
UIScrollView
свойство, в котором загружаются все другие виды («представление содержимого»)
Контроллер представления отвечает за инициализацию наложения, установив его исходный кадр (где высота - высота экрана) и передавая ему контент, не более того.
Из этого метода -initWithFrame
наложение устанавливает себя с UIDynamicItemBehavior
. Он также создает некоторые объекты UICollisionBehavior
: один в верхней части экрана и один внизу внизу экрана в правой позиции y, чтобы верхняя часть наложения была частично видимой (как видно в первом кадре вашего GIF). A UIGravityBehavior
также настроен для того, чтобы наложение на нижнюю границу столкновений. Конечно, также установлен _animator = [[UIDynamicAnimator alloc...
.
Наконец:
_pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan)];
_pan.delegate = self;
_pan.cancelsTouchesInView = FALSE;
Класс наложения также имеет некоторые другие полезные методы, такие как изменение направления силы тяжести, так что покрышка может оказаться привязываются к верхней или нижней части экрана.
Обработчик _pan
использует UISnapBehavior
, чтобы сохранить наложение перемещения динамически вверх и вниз экрана под пальцем пользователя:
- (void)handlePan
{
[self handlePanFromPanGestureRecogniser:_pan];
}
- (void)handlePanFromPanGestureRecogniser:(UIPanGestureRecognizer *)pan
{
CGFloat d = [pan velocityInView:self.superview.superview].y;
CGRect r = self.frame;
r.origin.y = r.origin.y + (d*0.057);
if (r.origin.y < 20)
{
r.origin.y = 20;
}
else if (r.origin.y > [UIScreen mainScreen].bounds.size.height - PEEKING_HEIGHT)
{
r.origin.y = [UIScreen mainScreen].bounds.size.height - PEEKING_HEIGHT;
}
if (pan.state == UIGestureRecognizerStateEnded)
{
[self panGestureEnded];
}
else if (pan.state == UIGestureRecognizerStateBegan)
{
[self snapToBottom];
[self removeGestureRecognizer:_tap];
}
else
{
[_animator removeBehavior:_findersnap];
_findersnap = [[UISnapBehavior alloc] initWithItem:self snapToPoint:CGPointMake(CGRectGetMidX(r), CGRectGetMidY(r))];
[_animator addBehavior:_findersnap];
}
}
- (void)panGestureEnded
{
[_animator removeBehavior:_findersnap];
CGPoint vel = [_dynamicSelf linearVelocityForItem:self];
if (fabsf(vel.y) > 250.0)
{
if (vel.y < 0)
{
[self snapToTop];
}
else
{
[self snapToBottom];
}
}
else
{
if (self.frame.origin.y > (self.superview.bounds.size.height/2))
{
[self snapToBottom];
}
else
{
[self snapToTop];
}
}
}
Обертка содержимого прослушивает прокрутки событий, генерируемых представления содержимого:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//this is our fancy way of getting the pan to work when the scrollview is in the way
if (scrollView.contentOffset.y <= 0 && _dragging)
{
_shouldForwardScrollEvents = TRUE;
}
if (_shouldForwardScrollEvents)
{
if ([_delegate respondsToSelector:@selector(theContentWrapper:isForwardingGestureRecogniserTouches:)])
{
[_delegate theContentWrapper:self isForwardingGestureRecogniserTouches:scrollView.panGestureRecognizer];
}
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
_dragging = FALSE;//scrollviewdidscroll must not be called after this
if (scrollView.contentOffset.y <= 0 || _shouldForwardScrollEvents)
{
if ([_delegate respondsToSelector:@selector(theContentWrapperStoppedBeingDragged:)])
{
[_delegate theContentWrapperStoppedBeingDragged:self];
}
}
_shouldForwardScrollEvents = FALSE;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
_dragging = TRUE;
}
Как вы можете видеть, когда bool shouldForwardScrollEvents
является TRUE
, тогда мы отправляем scrollView.panGestureRecognizer
делегату обложки содержимого (наложение). Оверлей реализует методы делегата следующим образом:
- (void)theContentWrapper:(TheContentWrapper *)contentWrapper isForwardingGestureRecogniserTouches:(UIPanGestureRecognizer *)contentViewPan
{
[self handlePanFromPanGestureRecogniser:contentViewPan];
}
- (void)theContentWrapperStoppedBeingDragged:(TheContentWrapper *)contentWrapper
{
//because the scrollview internal pan doesn't tell use when it's state == ENDED
[self panGestureEnded];
}
Надеюсь, что по крайней мере некоторые из них полезны для кого-то!
I .. На самом деле это произошло недавно (если я правильно вас понимаю), и это было действительно «уродливо». Решение проблемы scrollview/pan состояло в том, чтобы пересылать соответствующие события из 'scrollview.panGestureRecognizer' в класс, который обрабатывал основной кастрюль, когда прокрутка прокручивалась до самого верха. –
Не могли бы вы опубликовать этот конкретный код, чтобы получить четкое представление? Или все, если вы сегодня чувствуете себя милосердием: P Использовали ли вы интерактивные переходы или subview только в одном контроллере? Я также видел, что в рамках, которые я связал, он использует внутренне «hitTest», а затем пересылает событие касания к одному контроллеру или другому, но я не знаю, возможно ли это в интерактивных переходах. – Sento
Это код работы, поэтому я не думаю, что могу опубликовать все это. Также у меня его нет сейчас, я попробую и фильтрую соответствующие части и отправлю ответ как можно скорее. Что касается других вопросов: наложение - это просто подкласс UIView, и переход использует 'UIKitDynamics' –