在UITableView中滚动时移动视图

时间:2014-08-19 00:41:26

标签: ios cocoa-touch uitableview uiview uiscrollview

我的UIView下面有UITableView

enter image description here

我想要做的是当用户开始在表格中滚动时让UITableView上方的视图向上移动(不在路上),以便为UITableView提供更多空间(当你再次向下滚动时下来)。

我知道这通常是通过表头视图完成的,但我的问题是我的表视图位于选项卡内(实际上它是使用TTSliddingPageviewcontroller实现的横向滚动页面视图)。因此,虽然我只有一个UIView,但有三个UITableView

是否可以手动完成此操作?我的第一个想法是将所有内容都放在UIScrollView中,但根据Apple的文档,我们不应该在UITableView内放置UIScrollView因为这会导致不可预测的行为。

9 个答案:

答案 0 :(得分:24)

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
 }

答案 1 :(得分:18)

由于UITableViewUIScrollView的子类,因此您的表格视图的委托可以接收UIScrollViewDelegate方法。

在表格视图的委托中:

- (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;
}

答案 2 :(得分:8)

斯威夫特3&amp; 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
    }

答案 3 :(得分:6)

更简单快捷的方法

- (void)scrollViewDidScroll:(UIScrollView *)scrollView 
{

    CGRect rect = self.view.frame;
    rect.origin.y =  -scrollView.contentOffset.y;
    self.view.frame = rect;

}

答案 4 :(得分:3)

有人要求我的解决方案的代码,所以我在这里作为答案发布。这个想法应归功于NobodyNada。

在我的UITableViewController中,我实现了这种委托方法:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"TableViewScrolled" object:nil userInfo:scrollUserInfo];
}

scrollUserInfoNSDictionary,我将UITableView与通知一起传递给我(我在viewDidLoad执行此操作,因此我只需要执行一次):

scrollUserInfo = [NSDictionary dictionaryWithObject:self.tableView forKey:@"scrollView"];

现在,在具有视图的视图控制器中,我想在滚动时离开屏幕,我在viewDidLoad中执行此操作:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleScroll:) name:@"TableViewScrolled" object:nil];

最后我有了方法:

- (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是一个实例变量CGFloat previousOffset;topVerticalConstraintNSLayoutConstraint,设置为IBOutlet。它从视图的顶部到超视图的顶部,初始值为0.

这不完美。例如,如果用户非常积极地向上滚动视图的移动可能会有点不稳定。大型观点的问题更严重;如果视图足够小则没有问题。

答案 5 :(得分:3)

我在最后一个解决方案中添加了一些约束,以防止在快速滚动的情况下出现一些奇怪的行为

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
}

答案 6 :(得分:1)

要创建这样的动画,

enter image description here

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
}
  

您必须使用 collectionView / tableView

更改 pageMenu.view

答案 7 :(得分:0)

我知道这篇文章很老了。我试过上面的解决方案,但是对我来说都没有尝试过我自己,希望它可以帮助你。这种情况很常见,因为苹果建议不要在任何ScrollView中使用TableViewController,因为编译器会因为响应而感到困惑,因为它将获得两个委托回调 - 一个来自ScrollViewDelegate而另一个来自UITableViewDelegate。

相反,我们可以使用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;
}

这里lastContentOffset是CGFloat定义为属性

View Heirarchy如下: ViewController - &gt; View包含ScrollView(其委托方法在上面定义) - &gt;包含TableView。

通过上面的代码,我们手动增加和减少表格视图的高度以及ScrollView的内容大小。

请记住禁用TableView的滚动。

答案 8 :(得分:0)

您可以这样:

将它们添加为您的类变量:

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

这出现在您的viewDidLoad()上:

tableView.delegate = self

然后将其添加为视图控制器的扩展名:

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

    }
}

但是请注意,滚动机制仅在TableView滚动时才起作用。滚动顶视图不会折叠或展开。

我制作了一个示例项目,您可以检查它,名为iOSFixedHeaderList