ios 在 UITableView 中滚动时移动视图
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25374090/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Move a view when scrolling in UITableView
提问by pajevic
I have a UIView
with a UITableView
below it:
我有一个UIView
与UITableView
它下面:
What I would like to do is to have the view above the UITableView
move up (out of the way) when the user starts scrolling in the table in order to have more space for the UITableView
(and come down when you scroll down again).
我想要做的是UITableView
当用户开始在表格中滚动时让上面的视图向上移动(移开),以便有更多的空间UITableView
(并在您再次向下滚动时向下滚动)。
I know that this is normally done with a table header view, but my problem is that my table view is inside a tab (actually it is a side-scrolling page view implemented using TTSliddingPageviewcontroller
). So while I only have one top UIView
there are three UITableView
s.
我知道这通常是通过表头视图完成的,但我的问题是我的表视图位于选项卡内(实际上它是使用 实现的横向滚动页面视图TTSliddingPageviewcontroller
)。所以虽然我只有一个顶部,UIView
但有三个UITableView
s。
Is it possible to accomplish this manually? My first thought is to put everything in a UIScrollView
, but according to Apple's documentation one should never place a UITableView
inside a UIScrollView
as this leads to unpredictable behavior.
是否可以手动完成此操作?我的第一个想法是将所有内容都放在 a 中UIScrollView
,但根据 Apple 的文档,永远不应将 aUITableView
放入 a 中,UIScrollView
因为这会导致不可预测的行为。
采纳答案by NobodyNada - Reinstate Monica
Since UITableView
is a subclass of UIScrollView
, your table view's delegate can receive UIScrollViewDelegate
methods.
由于UITableView
是 的子类UIScrollView
,您的表视图的委托可以接收UIScrollViewDelegate
方法。
In your table view's delegate:
在您的表视图的委托中:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
static CGFloat previousOffset;
CGRect rect = self.view.frame;
rect.origin.y += previousOffset - scrollView.contentOffset.y;
previousOffset = scrollView.contentOffset.y;
self.view.frame = rect;
}
回答by George Muntean
Solution for Swift (Works perfectly with bounce enabled for scroll view):
Swift 的解决方案(与为滚动视图启用反弹完美配合):
var oldContentOffset = CGPointZero
let topConstraintRange = (CGFloat(120)..<CGFloat(300))
func scrollViewDidScroll(scrollView: UIScrollView) {
let delta = scrollView.contentOffset.y - oldContentOffset.y
//we compress the top view
if delta > 0 && topConstraint.constant > topConstraintRange.start && scrollView.contentOffset.y > 0 {
topConstraint.constant -= delta
scrollView.contentOffset.y -= delta
}
//we expand the top view
if delta < 0 && topConstraint.constant < topConstraintRange.end && scrollView.contentOffset.y < 0{
topConstraint.constant -= delta
scrollView.contentOffset.y -= delta
}
oldContentOffset = scrollView.contentOffset
}
回答by Dvole
Swift 3 & 4:
斯威夫特 3 和 4:
var oldContentOffset = CGPoint.zero
let topConstraintRange = (CGFloat(0)..<CGFloat(140))
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let delta = scrollView.contentOffset.y - oldContentOffset.y
//we compress the top view
if delta > 0 && yourConstraint.constant > topConstraintRange.lowerBound && scrollView.contentOffset.y > 0 {
yourConstraint.constant -= delta
scrollView.contentOffset.y -= delta
}
//we expand the top view
if delta < 0 && yourConstraint.constant < topConstraintRange.upperBound && scrollView.contentOffset.y < 0{
yourConstraint.constant -= delta
scrollView.contentOffset.y -= delta
}
oldContentOffset = scrollView.contentOffset
}
回答by Ghulam Rasool Hashmi
More simple and fast approach
更简单快捷的方法
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGRect rect = self.view.frame;
rect.origin.y = -scrollView.contentOffset.y;
self.view.frame = rect;
}
回答by Exception
I added some constraints to the last solution to prevent some strange behaviours in case of fast scrolling
我在最后一个解决方案中添加了一些约束,以防止在快速滚动的情况下出现一些奇怪的行为
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let delta = scrollView.contentOffset.y - oldContentOffset.y
//we compress the top view
if delta > 0 && topConstraint.constant > topConstraintRange.lowerBound && scrollView.contentOffset.y > 0 {
searchHeaderTopConstraint.constant = max(topConstraintRange.lowerBound, topConstraint.constant - delta)
scrollView.contentOffset.y -= delta
}
//we expand the top view
if delta < 0 && topConstraint.constant < topConstraintRange.upperBound && scrollView.contentOffset.y < 0 {
topConstraint.constant = min(searchHeaderTopConstraint.constant - delta, topConstraintRange.upperBound)
scrollView.contentOffset.y -= delta
}
oldContentOffset = scrollView.contentOffset
}
回答by pajevic
Someone asked for the code for my solution so I am posting it here as an answer. The credit for the idea should still go to NobodyNada.
有人要求提供我的解决方案的代码,所以我将其发布在这里作为答案。这个想法的功劳应该归功于NobodyNada。
In my UITableViewController
I implement this delegate method:
在我的UITableViewController
我实现这个委托方法:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
[[NSNotificationCenter defaultCenter] postNotificationName:@"TableViewScrolled" object:nil userInfo:scrollUserInfo];
}
scrollUserInfo
is a NSDictionary
where I put my UITableView
to pass it with the notification (I do this in viewDidLoad
so I only have to do it once):
scrollUserInfo
是NSDictionary
我把UITableView
它和通知一起传递的地方(我这样做,viewDidLoad
所以我只需要做一次):
scrollUserInfo = [NSDictionary dictionaryWithObject:self.tableView forKey:@"scrollView"];
Now, in the view controller that has the view I want to move off screen while scrolling I do this in viewDidLoad
:
现在,在具有我想在滚动时移出屏幕的视图的视图控制器中,我执行以下操作viewDidLoad
:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleScroll:) name:@"TableViewScrolled" object:nil];
And finally I have the method:
最后我有方法:
- (void)handleScroll:(NSNotification *)notification {
UIScrollView *scrollView = [notification.userInfo valueForKey:@"scrollView"];
CGFloat currentOffset = scrollView.contentOffset.y;
CGFloat height = scrollView.frame.size.height;
CGFloat distanceFromBottom = scrollView.contentSize.height - currentOffset;
if (previousOffset < currentOffset && distanceFromBottom > height) {
if (currentOffset > viewHeight)
currentOffset = viewHeight;
self.topVerticalConstraint.constant += previousOffset - currentOffset;
previousOffset = currentOffset;
}
else {
if (previousOffset > currentOffset) {
if (currentOffset < 0)
currentOffset = 0;
self.topVerticalConstraint.constant += previousOffset - currentOffset;
previousOffset = currentOffset;
}
}
}
previousOffset
is an instance variable CGFloat previousOffset;
.
topVerticalConstraint
is a NSLayoutConstraint
that is set as a IBOutlet
. It goes from the top of the view to the top of its superview and the initial value is 0.
previousOffset
是一个实例变量CGFloat previousOffset;
。
topVerticalConstraint
是 aNSLayoutConstraint
设置为 a IBOutlet
。它从视图的顶部到其父视图的顶部,初始值为 0。
It's not perfect. For instance, if the user scrolls very aggressively up the movement of the view can get a bit jerky. The issue is worse for large views; if the view is small enough there is no problem.
这并不完美。例如,如果用户非常积极地向上滚动,则视图的移动可能会有点生涩。对于大视图,这个问题更糟;如果视图足够小,则没有问题。
回答by Yassine En
To create like this animation,
要创建这样的动画,
lazy var redView: UIView = {
let view = UIView(frame: CGRect(x: 0, y: 0, width:
self.view.frame.width, height: 100))
view.backgroundColor = .red
return view
}()
var pageMenu: CAPSPageMenu?
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(redView)
let rect = CGRect(x: 0, y: self.redView.frame.maxY, width: self.view.bounds.size.width, height:(self.view.bounds.size.height - (self.redView.frame.maxY)))
pageMenu?.view.frame = rect
self.view.addSubview(pageMenu!.view)
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = scrollView.contentOffset.y
if(offset > 100){
self.redView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 0)
}else{
self.redView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 100 - offset)
}
let rect = CGRect(x: 0, y: self.redView.frame.maxY, width: self.view.bounds.size.width, height:(self.view.bounds.size.height - (self.redView.frame.maxY)))
pageMenu?.view.frame = rect
}
you must change pageMenu.viewwith your collectionView/tableView
您必须使用collectionView/tableView更改pageMenu.view
回答by Rodrigo
You can make it like this:
你可以这样做:
Add these as your class variables:
将这些添加为您的类变量:
private let NUMBER_OF_ROWS: Int = 256
private let ROW_HEIGHT: CGFloat = 75.0
private let MINIMUM_CONSTANT_VALUE: CGFloat = -150.0 /// This is the hidable view's height
@IBOutlet weak var hidableViewTopConstraint: NSLayoutConstraint! /// This is an outlet from your storyboard
private var lastContentOffset: CGFloat = 0.0
This goes on your viewDidLoad()
:
这继续你的viewDidLoad()
:
tableView.delegate = self
And then you add this as an extension for your view controller:
然后将其添加为视图控制器的扩展:
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let delta = tableView.contentOffset.y - lastContentOffset
let canScrollUp: Bool =
delta < 0 &&
hidableViewTopConstraint.constant < 0 &&
scrollView.contentOffset.y < 0
let canScrollDown: Bool =
delta > 0 &&
hidableViewTopConstraint.constant > MINIMUM_CONSTANT_VALUE &&
tableView.contentOffset.y > 0
if canScrollUp || canScrollDown{
hidableViewTopConstraint.constant -= delta
tableView.contentOffset.y -= delta
}
lastContentOffset = tableView.contentOffset.y
}
}
But note that the scroll mechanism will only work when the TableView is scrolled. The top view wont collapse or expand if you scroll it.
但请注意,滚动机制仅在 TableView 滚动时才起作用。如果滚动它,顶视图不会折叠或展开。
I made an example project you can check, it is called iOSFixedHeaderList
我做了一个你可以检查的示例项目,它叫做iOSFixedHeaderList
回答by Rishabh Sanghvi
I know this post in very old. I tried above solutions but neither worked for me for tried my own, hopefully it can help you. This scenario is pretty common, as apple suggested not to use TableViewController inside any ScrollView because the compiler will confused as in whom to respond becuase it will be getting two delegate call back - one from ScrollViewDelegate and another from UITableViewDelegate.
我知道这个帖子很老了。我尝试了上述解决方案,但对我自己的尝试都不奏效,希望它可以帮助您。这种情况很常见,因为苹果建议不要在任何 ScrollView 中使用 TableViewController,因为编译器会混淆响应谁,因为它将获得两个委托回调 - 一个来自 ScrollViewDelegate,另一个来自 UITableViewDelegate。
Instead we can use ScrollViewDelegate and disable the UITableViewScrolling.
相反,我们可以使用 ScrollViewDelegate 并禁用 UITableViewScrolling。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat currentOffSetY = scrollView.contentOffset.y;
CGFloat diffOffset = self.lastContentOffset - currentOffSetY;
self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, 400 + [self.tableView contentSize].height);
if (self.lastContentOffset < scrollView.contentOffset.y) {
tableView.frame = CGRectMake(tableView.frame.origin.x, tableView.frame.origin.y , tableView.frame.size.width, tableView.size.height - diffOffset);
}
if (self.lastContentOffset > scrollView.contentOffset.y) {
tableView.frame = CGRectMake(tableView.frame.origin.x, tableViewframe.origin.y, tableViewframe.size.width, tableView.frame.size.height + diffOffset);
}
self.lastContentOffset = currentOffSetY;
}
Here lastContentOffset is CGFloat defined as property
这里 lastContentOffset 是 CGFloat 定义为属性
The View Heirarchy is as follows: ViewController --> View contains ScrollView (whose delegate method is defined above) --> Contain TableView.
View Heirarchy如下:ViewController --> View包含ScrollView(其委托方法在上面定义)-->包含TableView。
By the above code we are manually increasing and decreasing the height of the table view along with the content size of ScrollView.
通过上面的代码,我们手动增加和减少了表格视图的高度以及 ScrollView 的内容大小。
Remember to disable the Scrolling of TableView.
记得禁用 TableView 的滚动。