简体   繁体   English

滚动/调整UITableView的大小

[英]scrolling/resizing UITableView

I'll get right to the point. 我会讲到重点。

I have a UIViewController that has two subviews in it. 我有一个UIViewController,其中有两个子视图。 The top one (let's call it HeaderView from now one) is a custom UIView and the bottom one is a UITableView. 顶部的是一个自定义的UIView(下面将其称为HeaderView),而底部的是一个UITableView。
I have set them up in InterfaceBuilder so that the HeaderView has 0 margin from the left, top and right, plus it has a fixed height. 我已经在InterfaceBuilder中对其进行了设置,以便HeaderView的左侧,顶部和右侧的边距为0,并且其高度固定。 The UITableView is directly underneath with 0 margin from all sides. UITableView在所有面的正下方,且页边距为0。

My goal is to achieve a behaviour such that when I start scrolling the UITableView's content the HeaderView will start shrinking and the UITableView becomes higher without scrolling. 我的目标是实现一种行为,当我开始滚动UITableView的内容时,HeaderView将开始缩小,并且UITableView会变得更高而无需滚动。 This should go on until the HeaderView has reached a minimum height. 这应该一直进行到HeaderView达到最小高度为止。 After that the UITableView should start scrolling as normal. 之后,UITableView应该开始正常滚动。 When scrolling down the effect should be reversed. 向下滚动时,效果应相反。

I have initially started this out using a UIScrollView instead of the UITableView and I have achieved the desired result. 我最初是使用UIScrollView而不是UITableView来开始的,并且已经达到了预期的结果。 Here is how: 方法如下:

  1. connect the UIScrollView to the outlet 将UIScrollView连接到插座
    @IBOutlet weak var scrollView: UIScrollView!

  2. set the UIScrollViewDelegate in the controller's viewDidLoad() method self.scrollView.delegate = self and declared the UIViewController to conform to the protocol 在控制器的viewDidLoad()方法中设置UIScrollViewDelegate self.scrollView.delegate = self并声明UIViewController符合协议

  3. intercept when the UIScrollView scrolls: 在UIScrollView滚动时进行拦截:
    func scrollViewDidScroll(_ scrollView: UIScrollView) { self.adjustScrolling(offset: scrollView.contentOffset.y, scrollView: scrollView) }

  4. in my adjustScrolling(offset:scrollView:) method the "magic" happens 在我的adjustScrolling(offset:scrollView:)方法中,“魔术”发生了

Now let's look at what happens in this method. 现在让我们看看这种方法会发生什么。

    private func adjustScrolling(offset: CGFloat, scrollView: UIScrollView) {

        // bind value between 0 and max header scroll
        let actualOffset: CGFloat = offset < 0 ? 0 : (offset >= self.maxHeaderScroll ? self.maxHeaderScroll : offset)

        // avoid useless calculations
        if (actualOffset == self.currentOffset) {
            return
        }

        /**
         * Apply the vertical scrolling to the header
         */
        // Translate the header up to give more space to the scrollView
        let headerTransform = CATransform3DTranslate(CATransform3DIdentity, 0, -(actualOffset), 0)
        self.header.layer.transform = headerTransform
        // Adjust header's subviews to new size
        self.header.didScrollBy(actualOffset)

        /**
         * Apply the corrected vertical scrolling to the scrollView
         */
        // Resize the scrollView to fill all empty space
        let newScrollViewY = self.header.frame.origin.y + self.header.frame.height
        scrollView.frame = CGRect(
            x: 0,
            y: newScrollViewY,
            width: scrollView.frame.width,
            height: scrollView.frame.height + (scrollView.frame.origin.y - newScrollViewY)
        )
        // Translate the scrollView's content view down to contrast scrolling
        let scrollTransform = CATransform3DTranslate(CATransform3DIdentity, 0, (actualOffset), 0)
        scrollView.subviews[0].layer.transform = scrollTransform
        // Set bottom inset to show content hidden by translation
        scrollView.contentInset = UIEdgeInsets(
            top: 0,
            left: 0,
            bottom: actualOffset,
            right: 0
        )

        self.currentOffset = actualOffset
    }

If I haven't forgotten anything this should be enough to achieve the desired effect. 如果我没有忘记任何内容,这应该足以实现所需的效果。 Let me break it down: 让我分解一下:

  1. I calculate the actualOffset binding it between 0 and self.MaxHeaderScroll which is just 67 (I think, it's calculated dynamically but this doesn't really matter) 我计算actualOffset 0之间结合它self.MaxHeaderScroll这仅仅是67(我认为,它是动态计算的,但这并不重要)
  2. If I see that the actualOffset hasn't changed since the last time this function was called I don't bother to aplly any changes. 如果我发现自从上次调用此函数以来, actualOffset并没有发生变化,那么我就不会麻烦地进行任何更改。 This avoids some useless calculations. 这避免了一些无用的计算。
  3. I apply the scrolling to the header by translating it up with a CATransform3DTranslate on just the y axis by negative actualOffset . 我将滚动应用于标题,方法是将CATransform3DTranslate沿y轴actualOffset ,而负的actualOffset
  4. I call self.header.didScrollBy(actualOffset) so that the HeaderView can apply some visual changes internally. 我调用self.header.didScrollBy(actualOffset)以便HeaderView可以在内部应用一些可视更改。 This doesn't concearn the question though. 但这并没有掩盖问题。
  5. I resize the scrollView so that it keeps 0 margin from top and bottom now that the HeaderView is higher up. 我调整了scrollView的大小,以使HeaderView的高度在顶部和底部保持0页边距。
  6. I translate down the scrollView's content by the same actualOffset amount to contrast the scrolling. 我向下滚动scrollView的内容时,实际的actualOffset量相同,以对比滚动。 This piece is essential to the correct visual effect that I want to achieve. 对于我想要实现的正确视觉效果而言,这是必不可少的。 If I didn't do this, the scrollView would still resize correctly but the content would start scrolling right away, which I don't want. 如果我不这样做,scrollView仍会正确调整大小,但是内容将立即开始滚动,这是我不希望的。 It should only start scrolling once the HeaderView reaches it's minimum height. 仅应在HeaderView达到最小高度后才开始滚动。
  7. I now set a bottom inset in the scrollView so that I am able to scroll it all the way to the end. 现在,我在scrollView中设置一个底部插图,以便能够一直滚动到最后。 Without this, the last part of the scrollView would be cut off since the scrollView itself would think it reached the end of it's content. 否则,scrollView的最后一部分将被剪切掉,因为scrollView本身会认为它到达了其内容的末尾。
  8. Lastly I store the actualOffset for later comparison 最后,我存储了actualOffset供以后比较

As I said, this works fine. 正如我所说,这很好。 The problem arises when I switch from a UIScrollView to a UITableView. 当我从UIScrollView切换到UITableView时出现问题。 I assumed it would work since UITableView inherits from UIScrollView. 由于UITableView继承自UIScrollView,因此我认为它会起作用。
The only piece of code that doesn't work is the number 6. I don't really know what is going wrong so I will just list everything I have found out and/or noticed. 唯一不起作用的代码是数字6。我真的不知道出了什么问题,所以我只列出我发现和/或注意到的所有内容。 Hopefully someone will be able to help me out. 希望有人能够帮助我。

  • in the case of the UIScrollView, in point 6, the scrollView.subviews[0] refers to a view that holds all the content inside it. 对于UIScrollView,在点6中, scrollView.subviews[0]引用一个视图,其中包含所有内容。 When I change to UITableView this subview seems to be of the type UITableViewWrapperView which I could not find any documentation about, nor does XCode recognize it as a valid class. 当我更改为UITableView时,此子视图似乎是UITableViewWrapperView类型,找不到任何文档,XCode也不将其识别为有效类。 This is already frustrating. 这已经令人沮丧。
  • if in point 6 I also give some translation on the x axis (let's say of 50) I can see an initial very quick translation that is immediately brought back to 0. This only happens when the UITableView starts scrolling, it doesn't go on while scrolling. 如果在第6点中,我还在x轴上进行了一些平移(比方说50),则可以看到初始的非常快速的平移立即变为0。这仅在UITableView开始滚动时才会发生,并且不会继续滚动时。
  • I have tried changing the frame of the subview in point 6 to achieve the desired result. 我尝试在第6点更改子视图的框架以实现所需的结果。 Although the scrolling is correct, the top cells start disappearing as I scroll the UITableView. 尽管滚动是正确的,但是当我滚动UITableView时,顶部的单元格开始消失。 I thin this is because I am using dequeueReusableCell(withIdentifier:for:) to instatiate the cells and the UITableView thinks that the top cells aren't visible when they actually are. 我之所以稀疏是因为我正在使用dequeueReusableCell(withIdentifier:for:)实例化单元格,并且UITableView认为顶部的单元格实际上并不可见。 I wasn't able to work around this problem. 我无法解决此问题。
  • I have tried setting the self.tableView.tableHeaderView to a UIView of the actualOffset height to contrast scrolling but this gave a weird effect where the cells would not scroll correctly and when the UITableView was brought back to the initial position, there would be a gap on top. 我尝试将self.tableView.tableHeaderView设置为actualOffset高度的UIView进行对比度滚动,但这产生了怪异的效果,即单元格无法正确滚动,并且当UITableView返回初始位置时,会出现间隙在上面。 No clue about this either. 也没有任何线索。

I know there's a lot here so please don't hesitate asking for more details. 我知道这里有很多东西,所以请随时询问更多详细信息。 Thank you in advance. 先感谢您。

I made something like this recently, so heres how I achieved it: 我最近做了这样的事情,所以这是我如何实现的:

Make a UIView with a height constraint constant and link this to your view/VC, have you UITableview constrained to the VC's view full screen behind the UIView. 使一个具有高度约束常量的UIView并将其链接到您的视图/ VC,将UITableview约束到UIView后面的VC全屏视图。

Now set your UITableViews contentInset top to the starting height of your 'headerView' now, in the scrollViewDidScroll you adjust the constant until the height of the header is at its minimum. 现在,将UITableViews contentInset顶部设置为“ headerView”的起始高度,在scrollViewDidScroll中,调整常量,直到标题的高度最小为止。

Here is a demo 这是一个演示

If you just run it, the blue area is your 'header' and the colored rows are just any cell. 如果只运行它,则蓝色区域是您的“标题”,而彩色行则是任何单元格。 You can autolayout whatever you want in the blue area and it should auto size and everything 您可以在蓝色区域中自动布局任何内容,它应该会自动调整大小和所有内容

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM