题 如何在UITableView透明标题下掩盖UITableViewCells


我希望标题掩盖单元格,但不是背景。

我有一个带有透明标题的UITableView和类似于Apple的通知中心的单元格(当你在iPhone的状态栏上向下滑动时)。我无法弄清楚如何屏蔽单元格,以便在滚动时它们不会显示在标题下方。

我已经尝试更改tableview的contentInsets,并且我已经尝试将标题View的框架更改为负面原点。


30
2017-08-26 02:13


起源


你在谈论表头或节标题吗? - RonLugge
@RonLugge他在谈论节标题。如上所述,当您打开通知中心时,您可以清楚地看到他的意思。 - Mazyod
我想知道这是一个透明的标题(在通知中心),还是只有一个与屏幕其余部分具有相同背景的标题?你可以通过这样做来解决你的问题,还是你的背景变得复杂起来? - rdelmar
@rdelmar通知中心的标题在移动时是透明的,我猜它根据位置不会改变。我的TableView样式与Apple Notification Center非常相似,它具有复杂的背景和多个部分,依赖阴影和高光来增加深度。将图形降级为不透明的标题将是我的最后手段。 - Jesse Gumpo


答案:


尝试创建一个子类 UITableviewCell 并添加这些方法

- (void)maskCellFromTop:(CGFloat)margin {
    self.layer.mask = [self visibilityMaskWithLocation:margin/self.frame.size.height];
    self.layer.masksToBounds = YES;
}

- (CAGradientLayer *)visibilityMaskWithLocation:(CGFloat)location {
    CAGradientLayer *mask = [CAGradientLayer layer];
    mask.frame = self.bounds;
    mask.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithWhite:1 alpha:0] CGColor], (id)[[UIColor colorWithWhite:1 alpha:1] CGColor], nil];
    mask.locations = [NSArray arrayWithObjects:[NSNumber numberWithFloat:location], [NSNumber numberWithFloat:location], nil];
    return mask;
}

并添加此委托方法 UITableView

#pragma mark - UIScrollViewDelegate

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    for (iNotifyTableViewCell *cell in self.visibleCells) {
        CGFloat hiddenFrameHeight = scrollView.contentOffset.y + [iNotifyHeaderView height] - cell.frame.origin.y;
        if (hiddenFrameHeight >= 0 || hiddenFrameHeight <= cell.frame.size.height) {
            [cell maskCellFromTop:hiddenFrameHeight];
        }
    }
}

*注意 [iNotifyHeaderView height] 是的高度 HeaderView。并使用 #import <QuartzCore/QuartzCore.h> 对于自定义单元格。


62
2017-12-06 03:06



谢谢!像魅力一样工作!我一整天都在苦苦挣扎。我不敢相信我必须为此链接QuartzCore.framework。但是,我也想复制你的代码,但是我无法知道掩码如何工作,你能解释一下吗? - htafoya
这应该被标记为正确的答案。 - nmdias
这是正确的答案。工作得很好! - zumzum
@AlexMarkman谢谢你,工作完美。我会添加tableView的顶部 contentInset 财产到 hiddenFrameHeight 您计算的是iOS 7添加到scrollViews的自动插入,以及刷新控件添加的任何空间等: CGFloat hiddenFrameHeight = scrollView.contentOffset.y + [iNotifyHeaderView height] - cell.frame.origin.y + scrollView.contentInset.top; - boliva
我还将此代码添加到UITableViewCell中 maskCellFromTop: 提高性能的方法(如果余量不适合当前单元格,则删除掩码) if (margin > 0) { self.layer.mask = [self visibilityMaskWithLocation:margin/self.frame.size.height]; self.layer.masksToBounds = YES; } else { self.layer.mask = nil; } - Alexander Tkachenko


对Alex Markman的回答进行一点编辑,您可以跳过为UITableViewCell创建子类。这种方法的好处是您可以将它用于多个不同的UITableViewCell。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    for (UITableViewCell *cell in self.tableView.visibleCells) {
        CGFloat hiddenFrameHeight = scrollView.contentOffset.y + self.navigationController.navigationBar.frame.size.height - cell.frame.origin.y;
        if (hiddenFrameHeight >= 0 || hiddenFrameHeight <= cell.frame.size.height) {
            [self maskCell:cell fromTopWithMargin:hiddenFrameHeight];
        }
    }
}

- (void)maskCell:(UITableViewCell *)cell fromTopWithMargin:(CGFloat)margin
{
    cell.layer.mask = [self visibilityMaskForCell:cell withLocation:margin/cell.frame.size.height];
    cell.layer.masksToBounds = YES;
}

- (CAGradientLayer *)visibilityMaskForCell:(UITableViewCell *)cell withLocation:(CGFloat)location
{
    CAGradientLayer *mask = [CAGradientLayer layer];
    mask.frame = cell.bounds;
    mask.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithWhite:1 alpha:0] CGColor], (id)[[UIColor colorWithWhite:1 alpha:1] CGColor], nil];
    mask.locations = [NSArray arrayWithObjects:[NSNumber numberWithFloat:location], [NSNumber numberWithFloat:location], nil];
    return mask;
}

16
2017-07-17 09:54



喜欢这个。像魅力一样工作。我喜欢我没有必要继承任何东西。我只需要修改self.navigationController.navigationBar.frame.size.height来使用我的表头的高度。 -rrh - Richie Hyatt
这是一个很好的解决方案,但存在一个问题。如果在加载数据时在表视图中使用某种分页,则掩码存在问题,即当tablefooterview显示分页的加载指示符隐藏时,它会隐藏最后一个单元格的底部。 - damjandd


@Alex Markman - 你的答案很棒,对我来说非常有用,但我发现当你滚动视网膜设备时,细胞不能平滑地滚动。我发现它是由图层引起的 locations 渲染过程中的参数:

mask.locations = [NSArray arrayWithObjects:[NSNumber numberWithFloat:location];

我稍微修改了你的代码。也许有人会发现它很有用:

- (void)maskCellFromTop:(CGFloat)margin
{
    self.layer.mask = [self visibilityMaskFromLocation:margin];
    self.layer.masksToBounds = YES;
}

- (CAGradientLayer *)visibilityMaskFromLocation:(CGFloat)location
{
    CAGradientLayer *mask = [CAGradientLayer layer];
    mask.frame = CGRectMake(
                            self.bounds.origin.x,
                            location+self.bounds.origin.y,
                            self.bounds.size.width,
                            self.bounds.size.height-location);
    mask.colors = @[
                    (id)[[UIColor colorWithWhite:1 alpha:1] CGColor],
                    (id)[[UIColor colorWithWhite:1 alpha:1] CGColor]
                    ];
    return mask;
}

11
2018-02-08 12:29





Swift版本

func scrollViewDidScroll(_ scrollView: UIScrollView) { 
    for cell in tableView.visibleCells {
        let hiddenFrameHeight = scrollView.contentOffset.y + navigationController!.navigationBar.frame.size.height - cell.frame.origin.y
        if (hiddenFrameHeight >= 0 || hiddenFrameHeight <= cell.frame.size.height) {
            maskCell(cell: cell, margin: Float(hiddenFrameHeight))
        }
    }
}

func maskCell(cell: UITableViewCell, margin: Float) {
    cell.layer.mask = visibilityMaskForCell(cell: cell, location: (margin / Float(cell.frame.size.height) ))
    cell.layer.masksToBounds = true
}

func visibilityMaskForCell(cell: UITableViewCell, location: Float) -> CAGradientLayer {
    let mask = CAGradientLayer()
    mask.frame = cell.bounds
    mask.colors = [UIColor(white: 1, alpha: 0).cgColor, UIColor(white: 1, alpha: 1).cgColor]
    mask.locations = [NSNumber(value: location), NSNumber(value: location)]
    return mask;
}

5
2017-10-18 11:42





Clean Swift 3版本:

extension YourViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        for cell in tableView.visibleCells {
            let hiddenFrameHeight = scrollView.contentOffset.y + navigationController!.navigationBar.frame.size.height - cell.frame.origin.y
            if (hiddenFrameHeight >= 0 || hiddenFrameHeight <= cell.frame.size.height) {
                if let customCell = cell as? CustomCell {
                    customCell.maskCell(fromTop: hiddenFrameHeight)
                }
            }
        }
    }
}

class CustomCell: UITableViewCell {
    public func maskCell(fromTop margin: CGFloat) {
        layer.mask = visibilityMask(withLocation: margin / frame.size.height)
        layer.masksToBounds = true
    }

    private func visibilityMask(withLocation location: CGFloat) -> CAGradientLayer {
        let mask = CAGradientLayer()
        mask.frame = bounds
        mask.colors = [UIColor.white.withAlphaComponent(0).cgColor, UIColor.white.cgColor]
        let num = location as NSNumber
        mask.locations = [num, num]
        return mask
    }
}

我只是用它,它就像一个魅力。我要感谢帖子中的每个人!四处投票!


3
2018-03-18 12:48





我有两个可能的解决方案,没有代码 - 只是想法:

  1. 不通用,应该与申请中心使用的安置/设计苹果一起使用。

使Section-Header不透明,'克隆'表格的背景图案作为section-Header的背景。根据节标题偏移量定位背景图案。


  1. 但是可能存在更多的性能问题。几个细胞应该可以正常工作。

为所有单元格层添加Alpha蒙版。根据细胞位置移动Alpha Mask。


(使用scrollViewDidScroll委托方法来维护背景图案/ Alpha-Mask偏移)。


0
2017-08-27 07:06





我最终将节标题的高度设置为最小值,并覆盖UITableView的layoutSubviews,将标题放在tableView的超视图上,将帧的原点向上调整高度。


0
2017-09-02 19:57



我们能看到一些代码吗?我有同样的要求。 - Jasper Blues
但这仅适用于一个部分页眉或页脚。 - htafoya
不是问题的答案。 - nmdias
其他答案更好。这篇文章是一个OP只是自私的正确答案。 - Sethmr