如何在UITableView表头上方添加带有视差效果的图片并保持表头固定?

15

这里有一张图片,说明了我想做的所有事情:

图片描述

我的问题是,如何构建我的视图结构。表格视图的标题应该固定在表格顶部。但是关于最上面的图片,在表格视图标题上面的那个图片,我需要怎么处理呢?我需要把表格视图放在UIScrollView中吗?

可以通过CATransform3D实现视差效果,但我想要实现的是什么,这就是我的问题。有很多演示文稿,但我想自己制作。


3
那么,你最终用了什么? - Dzior
1
所以,最终你实现了上述效果。如果是的话,能否分享一下样例代码给我? - AG007
@AmanGupta007 - 你解决了上面的演示问题吗? - Ram S
@Dzior - 你有没有解决上面那个演示的问题? - Ram S
我想我最终修改了APParallaxHeader项目。但那是一段时间之前的事了,我真的记不清了。 - Dzior
也许有点晚了,但我已经发布了一个答案。 - Reimond Hill
3个回答

7
你可以像这样将图像视图添加到视图中 -
let imageView = UIImageView()
let lblName = UILabel()

imageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 300)
imageView.image = UIImage.init(named: "poster")
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
view.addSubview(imageView)

lblName.frame = CGRect(x: 20, y: 100, width: 200, height: 22)
lblName.text = "Steve Jobs"
lblName.textColor = UIColor.white
lblName.font = UIFont.systemFont(ofSize: 26)
lblName.clipsToBounds = true
imageView.addSubview(lblName)

然后在tableview的代理方法中可以添加scrollviewDidScroll方法,如下所示 -

let y = 300 - (scrollView.contentOffset.y + 300)
let height = min(max(y, 60), 400)
imageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: height)
lblName.frame = CGRect(x: 20, y: height - 30, width: 200, height: 22)

我希望这会有所帮助。如果我有错误,请让我知道。 在此输入图像描述

这在 iPhone X 格式下会有什么变化?!在这个例子中,iPhone X 屏幕顶部会出现图像裁剪... - Famic Tech
@FamicTech你能再解释清楚一点吗?因为在我的iPhone X上也是一样的。 - JAINESH DOSHI
你有可供下载的示例应用程序吗?在iPhone X上,由于相机挡住了整个屏幕,通常图像会在左侧(时钟/时间)和右侧(电池指示器、WiFi和蜂窝网络指示器)被裁剪。 - Famic Tech
不,这是视差效果的演示。您可以使用答案中的代码检查此功能。如果您有任何疑问,请告诉我。它在iPhone X上的运行情况与gif或其他iPhone上的外观相同。 - JAINESH DOSHI
如果有导航栏,我们该如何实现它? - jayant rawat
显示剩余3条评论

3

Swift 5

按照我的需求,我使用了https://github.com/maxep/MXParallaxHeader

我会逐步向您解释事情的步骤。

您需要使用以下pod命令安装上述提到的第三方库:

1.)

pod "MXParallaxHeader"

打开命令管理器(终端),进入目标文件夹并运行以下命令:
2.)

pod install

您需要实现图像视图的视差效果并将标题固定在顶部,需要创建自定义的 .xib 文件作为视差标头。
3.) 

Add new file choose a (User Interface) View as a new template and name the 
file. eg.. ParallaxView and tap on the create.

你已经创建了UIView,现在需要为你的自定义视图添加Cocoa Touch类文件。

4.) 

Add new file choose a (Cocoa Touch Class) View as a new template and name the file. eg.. ParallaxView and tap on the Next.

现在您有一对类文件和其自定义的UIView例如(ParallaxView.xib 和 ParallaxView.swift)

根据我的项目要求,我需要在视差标题底部添加一个页面菜单,因此我使用另一个第三方库CAPSPageMenu

5.)

just visit this https://github.com/PageMenu/PageMenu/blob/master/Classes/CAPSPageMenu.swift and download the CAPSPageMenu.swift file and drag from your downloads and drop to your project destination folder.

现在我们准备开始编写代码部分。

进入您的ViewController文件并导入框架。

6.)

import MXParallaxHeader

委托方法

 7.)

 class MyParralax: UIViewController, MXScrollViewDelegate, CAPSPageMenuDelegate 
 {// Parant Controller Code }

为控制器(页面菜单)和 MXParallaxHeader 定义类 (MyParallax.swift) 变量的方法如下:

var scrollView      : MXScrollView!
let Parallax        = Bundle.main.loadNibNamed("ParallaxView", owner: nil, options: nil)?.first as? ParallaxView
let controller1     : VC1 = VC1.instantiateFromStoryboard()
let controller2     : VC2 = VC2.instantiateFromStoryboard()
var controllerArray : [UIViewController] = []
var pageMenu        : CAPSPageMenu?

作为Pagemenu的子视图控制器,您需要创建两个视图控制器文件,并在故事板中进行设置。这两个控制器(VC1和VC2)应该长成这个样子。

import UIKit

class VC1: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
       // child conroller 
    }

    class func instantiateFromStoryboard() -> VC1
    {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        return storyboard.instantiateViewController(withIdentifier: "VC1") as! VC1
    }
}

将这三个函数放在您的父控制器(MyParralax.swift)中:
func setParallaxMenu(){
        self.scrollView = MXScrollView()
        self.scrollView.backgroundColor  = UIColor.green
        self.scrollView.delegate = self
        self.scrollView.parallaxHeader.view = Parallax // You can set the parallax header view from a nib.
        self.scrollView.parallaxHeader.height = 446.0 // desired hieght or hight of the xib file
        self.scrollView.parallaxHeader.mode = MXParallaxHeaderMode.fill
        self.scrollView.parallaxHeader.minimumHeight = UIApplication.shared.statusBarFrame.size.height + (self.navigationController?.navigationBar.frame.height)!
        let newFrame = CGRect(x: 0,y: UIApplication.shared.statusBarFrame.size.height + (self.navigationController?.navigationBar.frame.height)!, width: self.view.frame.size.width, height: self.view.frame.size.height - (UIApplication.shared.statusBarFrame.size.height + (self.navigationController?.navigationBar.frame.height)!)) // scrollview's frame calculation
        scrollView.frame = newFrame
        scrollView.contentSize = newFrame.size
        self.scrollView.delegate = self
        view.addSubview(scrollView)
        self.pagemenuSetup()
    }


func pagemenuSetup()
    {
        controllerArray.removeAll()
        controllerArray.append(controller1)
        controllerArray.append(controller2)

        controller1.title = "ORANGE"
        controller2.title = "YELLOW"


        // Customize menu (Optional)
        let parameters: [CAPSPageMenuOption] = [
            .menuItemSeparatorWidth(4.3),
            .scrollMenuBackgroundColor(UIColor(red: 25.0/255.0, green: 26.0/255.0, blue: 36.0/255.0, alpha: 1.0)),
            .viewBackgroundColor(UIColor.clear),
            .selectionIndicatorColor(UIColor.white),
            .bottomMenuHairlineColor(UIColor.clear),
            .unselectedMenuItemLabelColor(UIColor(red: 255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 0.5)),
            .menuItemFont(UIFont(name: "Helvetica", size: 16.0)!),
            .enableHorizontalBounce(false),
            .menuHeight(52.0),
            .menuMargin(0.0),
            .menuItemWidth(self.view.bounds.width/2),
            .selectionIndicatorHeight(15.0),
            .menuItemSeparatorPercentageHeight(0.1),
            .iconIndicator(true),
            .iconIndicatorView(self.getIndicatorView())
        ]
        // Initialize scroll menu
        var frame = view.frame
        scrollView.frame = frame
        scrollView.contentSize = frame.size
        let Height = self.view.frame.size.height - (UIApplication.shared.statusBarFrame.size.height + (self.navigationController?.navigationBar.frame.height)!)
        frame.size.height = Height
        self.pageMenu = CAPSPageMenu(viewControllers: controllerArray, frame: frame, pageMenuOptions: parameters)
        pageMenu!.delegate = self
        self.scrollView.addSubview(pageMenu!.view)
        view.addSubview(scrollView)
    }


private func getIndicatorView()->UIView
    {
        let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width/2, height: 15.0))
        imgView.image = UIImage(named: "Indicator")
        imgView.contentMode = .scaleAspectFit
        return imgView
    }

请检查此输出。

在这里输入图片描述


当我们在每个选项卡中有一个TableView时,该如何处理。在我的情况下,TableView和ScrollView的滚动发生了冲突。 - amodkanthe
@amodkanthe 问题是你可以禁用UITableview的滚动。 - Azharhussain Shaikh
很遗憾,MXParallaxHeader的作者已经停止维护。不过,这个库有一个名为HPParallaxHeader的Swift版本可供选择:https://github.com/ngochiencse/HPParallaxHeader - Hien Pham

2
我想知道如何实现视差粘性标头,我找到了这篇文章来解决问题。
这篇文章是用Swift 2写的,但我已经重写成Swift 4.2。 CustomHeaderView
import UIKit

class CustomHeaderView: UIView {

    //MARK:- Variables
    //MARK: Constants


    //MARK: Variables
    var imageView:UIImageView!
    var colorView:UIView!
    var bgColor = UIColor(red: 235/255, green: 96/255, blue: 91/255, alpha: 1)
    var titleLabel = UILabel()
    var articleIcon:UIImageView!



    //MARK:- Constructor
    init(frame:CGRect, title: String) {

        self.titleLabel.text = title.uppercased()
        super.init(frame: frame)

        setUpView()

    }

    required init?(coder aDecoder: NSCoder) {

        fatalError("init(coder:) has not been implemented")

    }



    //MARK:- Private methods
    private func setUpView() {
        backgroundColor = UIColor.white

        imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(imageView)

        colorView = UIView()
        colorView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(colorView)

        let constraints:[NSLayoutConstraint] = [
            imageView.topAnchor.constraint(equalTo: self.topAnchor),
            imageView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
            imageView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
            imageView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
            colorView.topAnchor.constraint(equalTo: self.topAnchor),
            colorView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
            colorView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
            colorView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
        ]
        NSLayoutConstraint.activate(constraints)


        imageView.image = UIImage(named: "bg-header")
        imageView.contentMode = .scaleAspectFill

        colorView.backgroundColor = bgColor
        colorView.alpha = 0.6

        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(titleLabel)
        let titlesConstraints:[NSLayoutConstraint] = [
            titleLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor),
            titleLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 28),
            ]
        NSLayoutConstraint.activate(titlesConstraints)

        titleLabel.font = UIFont.systemFont(ofSize: 15)
        titleLabel.textAlignment = .center

        articleIcon = UIImageView()
        articleIcon.translatesAutoresizingMaskIntoConstraints = false
        addSubview(articleIcon)
        let imageConstraints:[NSLayoutConstraint] = [
            articleIcon.centerXAnchor.constraint(equalTo: self.centerXAnchor),
            articleIcon.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 6),
            articleIcon.widthAnchor.constraint(equalToConstant: 40),
            articleIcon.heightAnchor.constraint(equalToConstant: 40)
        ]

        NSLayoutConstraint.activate(imageConstraints)
        articleIcon.image = UIImage(named: "article")
    }


    //MARK:- Public methods
    func decrementColorAlpha(offset: CGFloat) {

        if self.colorView.alpha <= 1 {

            let alphaOffset = (offset/500)/85
            self.colorView.alpha += alphaOffset

        }
    }

    func decrementArticleAlpha(offset: CGFloat) {

        if self.articleIcon.alpha >= 0 {

            let alphaOffset = max((offset - 65)/85.0, 0)
            self.articleIcon.alpha = alphaOffset

        }

    }

    func incrementColorAlpha(offset: CGFloat) {

        if self.colorView.alpha >= 0.6 {

            let alphaOffset = (offset/200)/85
            self.colorView.alpha -= alphaOffset

        }

    }

    func incrementArticleAlpha(offset: CGFloat) {

        if self.articleIcon.alpha <= 1 {

            let alphaOffset = max((offset - 65)/85, 0)
            self.articleIcon.alpha = alphaOffset

        }

    }

}

然后是VieController

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    //MARK:- Variables
    //MARK: Constants


    //MARK: Variables
    var tableView:UITableView!
    var headerView:CustomHeaderView!
    var headerHeightConstraint:NSLayoutConstraint!



    //MARK: - Lifecycle methods
    override func viewDidLoad() {
        super.viewDidLoad()

        setUpHeader()
        setUpTableView()

    }



    //MARK: - Private methods
    private func setUpHeader() {

        headerView = CustomHeaderView(frame: CGRect.zero, title: "Articles")
        headerView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(headerView)

        headerHeightConstraint = headerView.heightAnchor.constraint(equalToConstant: 150)
        headerHeightConstraint.isActive = true

        let constraints:[NSLayoutConstraint] = [
            headerView.topAnchor.constraint(equalTo: view.topAnchor),
            headerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            headerView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ]

        NSLayoutConstraint.activate(constraints)

    }

    private func setUpTableView() {

        tableView = UITableView()
        tableView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tableView)

        let constraints:[NSLayoutConstraint] = [
            tableView.topAnchor.constraint(equalTo: headerView.bottomAnchor),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ]

        NSLayoutConstraint.activate(constraints)
        tableView.register(UITableViewCell.self,forCellReuseIdentifier: "cell")

        tableView.dataSource = self
        tableView.delegate = self

    }

    private func animateHeader() {

        self.headerHeightConstraint.constant = 150
        UIView.animate(withDuration: 0.4, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5, options: [.curveEaseInOut], animations: {
            self.view.layoutIfNeeded()
        }, completion: nil)

    }



    //MARK: - UITableView implementation
    //MARK: UITableViewDataSource implementation
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 100
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "cell",   for: indexPath)
        cell.textLabel?.text = "Article \(indexPath.row)"
        return cell

    }


    //MARK: UITableViewDelegate implementation
    func scrollViewDidScroll(_ scrollView: UIScrollView) {

        if scrollView.contentOffset.y < 0 {

            self.headerHeightConstraint.constant += abs(scrollView.contentOffset.y)
            headerView.incrementColorAlpha(offset: self.headerHeightConstraint.constant)
            headerView.incrementArticleAlpha(offset: self.headerHeightConstraint.constant)

        }
        else if scrollView.contentOffset.y > 0 && self.headerHeightConstraint.constant >= 65 {

            self.headerHeightConstraint.constant -= scrollView.contentOffset.y/100
            headerView.decrementColorAlpha(offset: scrollView.contentOffset.y)
            headerView.decrementArticleAlpha(offset: self.headerHeightConstraint.constant)

            if self.headerHeightConstraint.constant < 65 {
                self.headerHeightConstraint.constant = 65
            }

        }

    }

    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {

        if self.headerHeightConstraint.constant > 150 {
            animateHeader()
        }

    }

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {

        if self.headerHeightConstraint.constant > 150 {
            animateHeader()
        }

    }

}

根据视频显示的链接结果,接下来的步骤将添加安全区域约束,并可能将nib添加到标题中,但完全取决于您。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接