iOS 7 - 布局建议
iOS 7 - Layout advice
我的视图控制器中有以下布局。我希望能够垂直滚动 header 滚动视图和 UISegmentedControl 粘在视图的顶部,除此之外剩余的滚动应该由 Collection 处理看法。
不过,对于实现此布局的最佳方法是什么,我有点困惑。
我尝试了一些结果喜忧参半的实现:
UIScrollView with UICollectionView as subviews: UIScrollView as parent 视图与 header、分段控件和 collection 视图作为 child 控件。这种方法的问题是嵌套滚动似乎不能正常工作。为了能够滚动 UIScrollView,点击需要在 CollectionView 区域之外,否则只有 CollectionView 滚动,header 和分段控件不会移动.
Header 和 Header 单元格中的分段控件: 我尝试了另一种方法,使用单个 CollectionView .我将 header 和分段控件添加为 collection 视图的单个 Header 单元格的子视图。更改分段控件值时,我切换Collection视图的数据源属性,以实现collection视图所需的3个视图。视觉上一切都完美无缺。这里唯一的问题是在第一、第二和第三选项卡之间快速切换时的竞争条件。我从网络服务加载数据,如果网络服务需要时间并且仍在加载数据,我快速切换选项卡然后我 运行 进入错误,其中返回的数据用于不同的 collection 视图与当前选择的相比,很多乱序同步问题。
更新自动布局约束的常量值: 我尝试的另一种方法是更改应用于 "Header" 视图的自动布局约束的常量值.然后,我向视图控制器的视图添加了一个手势来跟踪滚动,当用户垂直滚动时,我调整了自动布局约束的常量,以便 "header" 单元格弹出视图。同样,这似乎并没有那么顺利,但我想我可以调整它,但它看起来有点像 hack。
有没有更好的方法来实现这个布局?
#2 似乎是一个很好的解决方案——滚动手势将最符合用户的期望,因为它都是一个滚动视图。 (我同意 #3 听起来像是 hack。)你可以用一些 custom layout attributes.
来制作 header "sticky"
The only problem here is the race condition when switching quickly between first, second and third tabs.
这是切换视图时异步加载的常见问题(尤其是当您将数据加载到单个单元格中时,这些单元格会在您滚动时重复使用)。重要的是,在收到数据后,您 始终检查 接收方是否仍在期待它;即,您应该在更改支持数据源之前检查分段控制值。您还可以:
- 为不同的细分使用单独的数据源 objects,让每个细分管理自己的数据获取,这样它们就不会混淆。
- 快速切换标签时,如果可以取消未完成的请求,避免不必要的网络请求。
- 每次切换标签时缓存数据以避免re-fetching。
我只能说你需要子类化 UIView 并使它成为 UIGestureRecognizerDelegate 和 UICollectionViewDelegate 的委托,然后在你的 UIView 子类中,执行以下操作,我不能再给出这方面的信息,因为代码,虽然归我所有,但它是专有的,可能会激怒我用过它的很多组织,所以这是秘诀:
CGPoint contentOffset = [scrollView contentOffset];
CGFloat newHeight = [_headerView maxHeight] - contentOffset.y;
CGRect frame = [_headerView frame];
if (newHeight > [_headerView maxHeight]) {
frame.origin.y = contentOffset.y;
frame.size.height = [_headerView maxHeight];
[_headerView setFrame:frame];
} else if (newHeight < [_headerView minHeight]) {
frame.origin.y = contentOffset.y;
frame.size.height = [_headerView minHeight];
[_headerView setFrame:frame];
} else {
frame.origin.y = contentOffset.y;
frame.size.height = newHeight;
[_headerView setFrame:frame];
}
if ([_delegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
return [_delegate scrollViewDidScroll:scrollView];
}
您必须将另一个 UIView 定义为此自定义 UiCollectionView 的 header。然后,您必须在 UIView/UICollectionView 委托的自定义子视图中声明 UIView 自定义 header 视图,然后在 UICollctionViewdelegate 中设置该自定义子视图的 header。然后,您应该将 UIView/UIcollectionView 的这个复合子类引入到您的 UIViewController 中。哦,是的,在您的 layoutSubViews 中,确保您进行了通过双层子类传递的高度计算。因此,您将拥有以下文件:
UIVew这是UICollectionView的delegate和我之前提到的
UIView 这是一个 UISCrollViewDelegate,这是 header 视图
UIViewController 拉入编号为 1 的子类 UIView
1 号的 UIView 子类,拉入 2 号并将其设置为其 header
在第 4 部分中,确保你这样做:
- (CGFloat)maxHeight
{
if (SCREEN_WIDTH == 414)
{
return 260;
}else if (SCREEN_WIDTH == 375)
{
return 325;
}else
{
return 290;
}
}
- (CGFloat)minHeight
{
if (SCREEN_WIDTH == 414)
{
return 90;
}else if (SCREEN_WIDTH == 375)
{
return 325;
}else
{
return 290;
}
}
这将传递给 UIView 子类,正如我已经解释过的那样,它是一个复合子类。这个想法是在这个 header UIView(上面的数字 2)的子类中捕获你 header 的 maxHeight,然后将它传递给在 scrollViewDidScroll 中拦截这些值的主 UIView 子类。
最后一点信息,确保在所有方法中设置 layoutSubviews 以拦截滚动事件。例如在上面的数字 1 中,layoutsubviews 方法是这样的:
- (void)layoutSubviews
{
CGRect frame = [_headerView frame];
frame.size.width = [self frame].size.width;
[_headerView setFrame:frame];
[super layoutSubviews];
}
这就是我能给你的全部,我希望我能 post 更多,但这应该让你了解它是如何在生产环境中为你在野外看到的大型应用程序完成的。
还有一点要注意。当你开始走上像这样密集实现的道路时,不要惊讶地发现,例如,一个应用程序中的单个视图控制器使用我已经解释过的方法将有 30-40 个自定义子类它们要么是自己的子类,要么是复合子类,或者是我自己的子类或我自己的子类的子类。我告诉你这个是为了让你了解需要多少代码才能做到这一点,不是为了吓唬你,而是让你知道这可能需要一段时间才能正确,并且不要踢自己的屁股如果需要一段时间才能完成工作。祝你好运!!
我认为您想要 pinterest 个人资料页面具有的相同功能。要以简单的方式实现这样的功能,您需要做以下事情。
第1步 : 添加UIView作为tableHeaderView 向上滚动时炫耀的那些。
self.tableHeaderView = yourView
步骤 2:在 header 视图部分添加 UISegmentControl
。
- (UITableViewHeaderFooterView *)headerViewForSection:(NSInteger)section{
return your_segmentcontrolView;
}
第 3 步:将 UICollectionView
添加到第一部分的第一行。
通过下面的方式实现,你可以得到你想要的功能。
希望对您有所帮助。
- 将 Header 视图、Body 视图(持有段视图和 Collection 视图)添加到滚动视图中。
- 最初将 collection 视图的 userInteractionEnabled 属性 设置为 "NO"。
- 始终跟踪滚动视图的昆虫。
- 如果滚动的昆虫y-coordinate大于header视图的高度,则将userInteractionEnabled属性设置为"YES",以便collection 视图可以滚动。
- 如果用户滚动到滚动视图之外并尝试将 header 视图拉低,即滚动视图 y-coordinate 昆虫小于 header 视图的高度,则立即更改collection 视图的用户迭代模式并允许用户将滚动视图滚动到顶部。
您可以考虑的替代方法:
使用 UITableView 来包含您的 UI
- 创建一个 UITableView,并将您的 header 设置为 UITableView 的 headerView。
- 使用 sectionHeader 包含 segmentedControl。
- 将您的 collectionView 放在单个 UITableViewCell 中。或者,您可以使用 UITableView 的 footerView 来包含 gridView。
通过使用 sectionHeader,这应该允许 header 滚动出视图,但是 sectionHeader 将停留在导航栏下方或 contentView 的顶部,直到另一个部分进入视图(在您的情况下)你只会有一个部分。)
您可以使用 library/cocoapod 为您设置,而不是手动实现。这个看起来很合适:https://github.com/iosengineer/BMFloatingHeaderCollectionViewLayout
此外,代码是开源的,因此您可以随时根据需要进行修改。
我的视图控制器中有以下布局。我希望能够垂直滚动 header 滚动视图和 UISegmentedControl 粘在视图的顶部,除此之外剩余的滚动应该由 Collection 处理看法。
不过,对于实现此布局的最佳方法是什么,我有点困惑。
我尝试了一些结果喜忧参半的实现:
UIScrollView with UICollectionView as subviews: UIScrollView as parent 视图与 header、分段控件和 collection 视图作为 child 控件。这种方法的问题是嵌套滚动似乎不能正常工作。为了能够滚动 UIScrollView,点击需要在 CollectionView 区域之外,否则只有 CollectionView 滚动,header 和分段控件不会移动.
Header 和 Header 单元格中的分段控件: 我尝试了另一种方法,使用单个 CollectionView .我将 header 和分段控件添加为 collection 视图的单个 Header 单元格的子视图。更改分段控件值时,我切换Collection视图的数据源属性,以实现collection视图所需的3个视图。视觉上一切都完美无缺。这里唯一的问题是在第一、第二和第三选项卡之间快速切换时的竞争条件。我从网络服务加载数据,如果网络服务需要时间并且仍在加载数据,我快速切换选项卡然后我 运行 进入错误,其中返回的数据用于不同的 collection 视图与当前选择的相比,很多乱序同步问题。
更新自动布局约束的常量值: 我尝试的另一种方法是更改应用于 "Header" 视图的自动布局约束的常量值.然后,我向视图控制器的视图添加了一个手势来跟踪滚动,当用户垂直滚动时,我调整了自动布局约束的常量,以便 "header" 单元格弹出视图。同样,这似乎并没有那么顺利,但我想我可以调整它,但它看起来有点像 hack。
有没有更好的方法来实现这个布局?
#2 似乎是一个很好的解决方案——滚动手势将最符合用户的期望,因为它都是一个滚动视图。 (我同意 #3 听起来像是 hack。)你可以用一些 custom layout attributes.
来制作 header "sticky"The only problem here is the race condition when switching quickly between first, second and third tabs.
这是切换视图时异步加载的常见问题(尤其是当您将数据加载到单个单元格中时,这些单元格会在您滚动时重复使用)。重要的是,在收到数据后,您 始终检查 接收方是否仍在期待它;即,您应该在更改支持数据源之前检查分段控制值。您还可以:
- 为不同的细分使用单独的数据源 objects,让每个细分管理自己的数据获取,这样它们就不会混淆。
- 快速切换标签时,如果可以取消未完成的请求,避免不必要的网络请求。
- 每次切换标签时缓存数据以避免re-fetching。
我只能说你需要子类化 UIView 并使它成为 UIGestureRecognizerDelegate 和 UICollectionViewDelegate 的委托,然后在你的 UIView 子类中,执行以下操作,我不能再给出这方面的信息,因为代码,虽然归我所有,但它是专有的,可能会激怒我用过它的很多组织,所以这是秘诀:
CGPoint contentOffset = [scrollView contentOffset];
CGFloat newHeight = [_headerView maxHeight] - contentOffset.y;
CGRect frame = [_headerView frame];
if (newHeight > [_headerView maxHeight]) {
frame.origin.y = contentOffset.y;
frame.size.height = [_headerView maxHeight];
[_headerView setFrame:frame];
} else if (newHeight < [_headerView minHeight]) {
frame.origin.y = contentOffset.y;
frame.size.height = [_headerView minHeight];
[_headerView setFrame:frame];
} else {
frame.origin.y = contentOffset.y;
frame.size.height = newHeight;
[_headerView setFrame:frame];
}
if ([_delegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
return [_delegate scrollViewDidScroll:scrollView];
}
您必须将另一个 UIView 定义为此自定义 UiCollectionView 的 header。然后,您必须在 UIView/UICollectionView 委托的自定义子视图中声明 UIView 自定义 header 视图,然后在 UICollctionViewdelegate 中设置该自定义子视图的 header。然后,您应该将 UIView/UIcollectionView 的这个复合子类引入到您的 UIViewController 中。哦,是的,在您的 layoutSubViews 中,确保您进行了通过双层子类传递的高度计算。因此,您将拥有以下文件:
UIVew这是UICollectionView的delegate和我之前提到的
UIView 这是一个 UISCrollViewDelegate,这是 header 视图
UIViewController 拉入编号为 1 的子类 UIView
1 号的 UIView 子类,拉入 2 号并将其设置为其 header
在第 4 部分中,确保你这样做:
- (CGFloat)maxHeight
{
if (SCREEN_WIDTH == 414)
{
return 260;
}else if (SCREEN_WIDTH == 375)
{
return 325;
}else
{
return 290;
}
}
- (CGFloat)minHeight
{
if (SCREEN_WIDTH == 414)
{
return 90;
}else if (SCREEN_WIDTH == 375)
{
return 325;
}else
{
return 290;
}
}
这将传递给 UIView 子类,正如我已经解释过的那样,它是一个复合子类。这个想法是在这个 header UIView(上面的数字 2)的子类中捕获你 header 的 maxHeight,然后将它传递给在 scrollViewDidScroll 中拦截这些值的主 UIView 子类。
最后一点信息,确保在所有方法中设置 layoutSubviews 以拦截滚动事件。例如在上面的数字 1 中,layoutsubviews 方法是这样的:
- (void)layoutSubviews
{
CGRect frame = [_headerView frame];
frame.size.width = [self frame].size.width;
[_headerView setFrame:frame];
[super layoutSubviews];
}
这就是我能给你的全部,我希望我能 post 更多,但这应该让你了解它是如何在生产环境中为你在野外看到的大型应用程序完成的。
还有一点要注意。当你开始走上像这样密集实现的道路时,不要惊讶地发现,例如,一个应用程序中的单个视图控制器使用我已经解释过的方法将有 30-40 个自定义子类它们要么是自己的子类,要么是复合子类,或者是我自己的子类或我自己的子类的子类。我告诉你这个是为了让你了解需要多少代码才能做到这一点,不是为了吓唬你,而是让你知道这可能需要一段时间才能正确,并且不要踢自己的屁股如果需要一段时间才能完成工作。祝你好运!!
我认为您想要 pinterest 个人资料页面具有的相同功能。要以简单的方式实现这样的功能,您需要做以下事情。
第1步 : 添加UIView作为tableHeaderView 向上滚动时炫耀的那些。
self.tableHeaderView = yourView
步骤 2:在 header 视图部分添加 UISegmentControl
。
- (UITableViewHeaderFooterView *)headerViewForSection:(NSInteger)section{
return your_segmentcontrolView;
}
第 3 步:将 UICollectionView
添加到第一部分的第一行。
通过下面的方式实现,你可以得到你想要的功能。
希望对您有所帮助。
- 将 Header 视图、Body 视图(持有段视图和 Collection 视图)添加到滚动视图中。
- 最初将 collection 视图的 userInteractionEnabled 属性 设置为 "NO"。
- 始终跟踪滚动视图的昆虫。
- 如果滚动的昆虫y-coordinate大于header视图的高度,则将userInteractionEnabled属性设置为"YES",以便collection 视图可以滚动。
- 如果用户滚动到滚动视图之外并尝试将 header 视图拉低,即滚动视图 y-coordinate 昆虫小于 header 视图的高度,则立即更改collection 视图的用户迭代模式并允许用户将滚动视图滚动到顶部。
您可以考虑的替代方法:
使用 UITableView 来包含您的 UI
- 创建一个 UITableView,并将您的 header 设置为 UITableView 的 headerView。
- 使用 sectionHeader 包含 segmentedControl。
- 将您的 collectionView 放在单个 UITableViewCell 中。或者,您可以使用 UITableView 的 footerView 来包含 gridView。
通过使用 sectionHeader,这应该允许 header 滚动出视图,但是 sectionHeader 将停留在导航栏下方或 contentView 的顶部,直到另一个部分进入视图(在您的情况下)你只会有一个部分。)
您可以使用 library/cocoapod 为您设置,而不是手动实现。这个看起来很合适:https://github.com/iosengineer/BMFloatingHeaderCollectionViewLayout
此外,代码是开源的,因此您可以随时根据需要进行修改。