如何使用 Scrollview(或最佳实践)进行 Card Deck 样式页面转换,垂直和水平
Posted
技术标签:
【中文标题】如何使用 Scrollview(或最佳实践)进行 Card Deck 样式页面转换,垂直和水平【英文标题】:How to do Card Deck style page transitioning, Vertically & Horizontally, using UIScrollview (or best practice) 【发布时间】:2012-12-23 10:47:46 【问题描述】:我正在创建一个可以在书的页面之间左右滑动的应用程序(像卡片组一样覆盖或显示),并且上下滑动将向下滑动(并覆盖)设置页面,或者滑动向上(并揭示)这本书的详细信息。
本质上,它是一个“卡片组”滑动功能,类似于我正在考虑使用的这个项目https://github.com/sweetmandm/CardSliderView-for-ios,但是我需要垂直和水平的“卡片组”滑动功能。
为您提供另一个我正在查看的幻灯片/左封面/显示效果的示例,请查看新的 CNN 应用程序,您可以在其中在文章之间滑动。
我也考虑过使用 UIPageViewController,但是这不支持我正在寻找的“滑动和显示/滑动和覆盖”过渡,而只支持“向左或向右滑动”过渡,所以我必须以某种方式破解它并使用多个 UIPageViewControllers,一个在上面,以允许“显示和覆盖”效果工作,仅使用 UIPageViewController 中的手势来允许用户滑动。
我熟悉 UIScrollview 上的 directionalLockEnabled 属性,但我仍然想知道获得我正在寻找的效果的整体最佳方法是什么,一种同时支持垂直和水平的 UIScrollView?页面视图控制器?嵌套的 UIScrollviews?而不是玩弄 directionalLockEnabled 属性?还有什么?
实现我希望提供的确切用户体验的最佳方式是什么?
【问题讨论】:
【参考方案1】:好的,我找到了解决我自己问题的方法,使用嵌套的 UIScrollViews。我还添加了一个非常粗略的解决方案作为项目到 github:https://github.com/cohen72/InfiniteDeckScroller。
对于水平滚动,我有三个水平滚动视图,一个嵌套在另一个中。 UIScrollview 会自动处理每个的正确滚动。根据内容偏移和正在滚动的滚动视图,我知道如何“重新排列/重新排序”嵌套的滚动视图。
这是我提出的解决方案的 sn-p。
这个特殊的解决方案允许向上滑动显示,但是我还没有实现向下滑动覆盖,但是这样做会使用与水平解决方案相同的方法。
#define BOTTOM 1
#define MIDDLE 2
#define TOP 3
#define VERTICAL_SUB 4
- (void)viewDidLoad
[super viewDidLoad];
UIScrollView *scroll1 = [[UIScrollView alloc] initWithFrame:self.view.bounds];
UIScrollView *scroll2 = [[UIScrollView alloc] initWithFrame:self.view.bounds];
UIScrollView *scroll3 = [[UIScrollView alloc] initWithFrame:self.view.bounds];
UIScrollView *scroll4 = [[UIScrollView alloc] initWithFrame:self.view.bounds];
scroll1.contentSize = CGSizeMake(self.view.frame.size.width * 2, self.view.frame.size.height * 1);
scroll1.tag = BOTTOM;
scroll1.pagingEnabled = YES;
scroll1.bounces = NO;
scroll1.delegate = self;
[scroll1 addSubview:[self labelForScrollView:scroll1 withBgColor:[UIColor redColor]]];
[scroll1 setContentOffset:CGPointMake(0, 0)];
scroll2.contentSize = CGSizeMake(self.view.frame.size.width * 2, self.view.frame.size.height * 1);
scroll2.tag = MIDDLE;
scroll2.pagingEnabled = YES;
scroll2.bounces = NO;
scroll2.delegate = self;
[scroll2 addSubview:[self labelForScrollView:scroll2 withBgColor:[UIColor orangeColor]]];
[scroll2 setContentOffset:CGPointMake(0, 0)];
scroll3.contentSize = CGSizeMake(self.view.frame.size.width * 2, self.view.frame.size.height * 1);
scroll3.tag = TOP;
scroll3.pagingEnabled = YES;
scroll3.bounces = NO;
scroll3.delegate = self;
[scroll3 addSubview:[self labelForScrollView:scroll3 withBgColor:[UIColor yellowColor]]];
[scroll3 setContentOffset:CGPointMake(320, 0)];
scroll4.contentSize = CGSizeMake(self.view.frame.size.width * 1, self.view.frame.size.height * 2);
scroll4.delegate = self;
scroll4.bounces = NO;
scroll4.pagingEnabled = YES;
scroll4.alwaysBounceVertical = NO;
scroll4.tag = VERTICAL_SUB;
[scroll4 addSubview:scroll1];
[scroll1 addSubview:scroll2];
[scroll2 addSubview:scroll3];
[self.view addSubview:scroll4];
- (UILabel*)labelForScrollView:(UIScrollView*)scrollView withBgColor:(UIColor*)color
UILabel *lbl = [[UILabel alloc] initWithFrame:scrollView.bounds];
lbl.textAlignment = NSTextAlignmentCenter;
lbl.backgroundColor = color;
lbl.text = [NSString stringWithFormat:@"ScrollView: %d", scrollView.tag];
return lbl;
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
NSLog(@"content offset: %f, tag: %d ", scrollView.contentOffset.x, scrollView.tag);
UIScrollView *newMiddleScrollView, *newBottomScrollView, *newTopScrollView;
// swipe left
if (scrollView.contentOffset.x == 0 && scrollView.tag == TOP)
newMiddleScrollView = (UIScrollView*)[self.view viewWithTag:TOP];
newTopScrollView = (UIScrollView*)[self.view viewWithTag:BOTTOM];
newBottomScrollView = (UIScrollView*)[self.view viewWithTag:MIDDLE];
// swipe right
else if (scrollView.contentOffset.x == 320 && scrollView.tag == MIDDLE)
newMiddleScrollView = (UIScrollView*)[self.view viewWithTag:BOTTOM];
newTopScrollView = (UIScrollView*)[self.view viewWithTag:MIDDLE];
newBottomScrollView = (UIScrollView*)[self.view viewWithTag:TOP];
else
return;
newMiddleScrollView.tag = MIDDLE;
newBottomScrollView.tag = BOTTOM;
newTopScrollView.tag = TOP;
newBottomScrollView.contentOffset = CGPointMake(0, 0);
newMiddleScrollView.contentOffset = CGPointMake(0, 0);
newTopScrollView.contentOffset = CGPointMake(320, 0);
UIScrollView *verticalScrollView_sub = (UIScrollView*)[self.view viewWithTag:VERTICAL_SUB];
[verticalScrollView_sub addSubview:newBottomScrollView];
[newBottomScrollView addSubview:newMiddleScrollView];
[newMiddleScrollView addSubview:newTopScrollView];
【讨论】:
【参考方案2】:这是一个很好的问题 - 如果我在这里错了,请纠正我,但听起来从左到右有一个 任意 数量的页面/卡片,但只有几张标准卡片进来从顶部或底部(您的设置和详细信息面板)。
如果是这种情况,那么您可能希望坚持使用类似于UIPageController
的东西以及一些手势识别器。你设置你的页面控制器(或者你最终使用的任何控制器来实现你想要的卡片动画),然后添加两个手势识别器来向上滑动和向下滑动。
然后,您可以在收到这些手势时为您的详细信息/设置视图设置动画,从而为您提供类似卡片的界面,而无需打扰多个 UIPageViewControllers
或滚动视图。
如果您想要水平和垂直的任意数量的卡片,这种方法不是很好,但听起来好像不是这样。
【讨论】:
没错,我正在水平滚动任意数量的卡片,并带有静态数量的向上/向下卡片。我在这里发布了一个使用嵌套 UIScrollViews 的解决方案,效果很好。我现在正在努力将它创建为一个具有数据源的类,该数据源将存储“卡片”并允许正确排序。【参考方案3】:有很多不同的方法可以实现这一点,具体取决于您希望不同的控制器如何相互关联。幻灯片转换本身非常简单。我已经在一个控制器中以这种方式实现了它,该控制器是调用该方法的控制器的超类:
-(void)SlideInController:(RDSlideController *) next
next.presentingVC = self;
next.view.frame = CGRectMake(self.view.frame.origin.x + 320, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height);
[self.view.window addSubview:next.view];
[UIView animateWithDuration:1 animations:^
next.view.frame = self.view.frame;
completion:^(BOOL finished)
self.view.window.rootViewController = next;
];
然后从新出现的控制器中,你可以调用这个方法返回:
-(void)SlideOut
UIViewController *prev = self.presentingVC;
prev.view.frame = self.view.frame;
[self.view.window insertSubview:prev.view belowSubview:self.view];
[UIView animateWithDuration:1 animations:^
self.view.frame = CGRectMake(self.view.frame.origin.x + 320, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height);
completion:^(BOOL finished)
self.view.window.rootViewController = prev;
];
【讨论】:
以上是关于如何使用 Scrollview(或最佳实践)进行 Card Deck 样式页面转换,垂直和水平的主要内容,如果未能解决你的问题,请参考以下文章
最佳实践|从Producer 到 Consumer,如何有效监控 Kafka
最佳实践|从Producer 到 Consumer,如何有效监控 Kafka
使用 @AppStorage 进行设置的 SwiftUI 最佳实践:如何在加载 @AppStorage 之前读取 UserDefaults?