导航栏标题中的多个叠加图像

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了导航栏标题中的多个叠加图像相关的知识,希望对你有一定的参考价值。

我知道如何在UINavigationBar中居单个图像,但不知道如何使用动态数量的图像。我有一个支持群聊的聊天应用。群聊中的人数可能只有3,但没有上限。

UINavigationBar中,我必须设置标题以显示至少4或5个叠加图像(但不超过它,因为它在UINavigationBar看起来很奇怪)和UILabel显示在同一组聊天中有多少用户(即+ 15 more) )。标题(所有图像和标签)应该以UINavigationBar为中心。正在从服务器下载图像。

当用户点击标题(任何图像或UINavigationBar中的标签)时,它应触发一个动作,以在单独的UIViewController中显示用户的完整列表

覆盖图像的数量是动态的(基于每个群聊),但我无法弄清楚如何做到这一点。以下是最终结果的图像:

enter image description here

有没有人之前做过这个或知道如何做到这一点?非常感谢帮助

更新:

我试图用UIStackView完成这个,但我有很多问题。这是代码:

    var navStackView : UIStackView = {
    let stack = UIStackView()
    stack.axis = .horizontal
    stack.backgroundColor = .red
    stack.alignment = .fill
    stack.distribution = .fillEqually
    stack.translatesAutoresizingMaskIntoConstraints = false
    return stack
}()

var images = ["1", "2", "3", "4"]

override func viewDidLoad() {
    super.viewDidLoad()

    let navController = navigationController!

    navController.navigationBar.addSubview(navStackView)
    // x, y, w, h
    navStackView.leadingAnchor.constraint(equalTo: navController.navigationBar.leadingAnchor).isActive = true
    navStackView.trailingAnchor.constraint(equalTo: navController.navigationBar.trailingAnchor).isActive = true
    navStackView.topAnchor.constraint(equalTo: navController.navigationBar.topAnchor).isActive = true
    navStackView.bottomAnchor.constraint(equalTo: navController.navigationBar.bottomAnchor).isActive = true


    for image in images {
        let imageView = UIImageView()
        imageView.image = UIImage(named: image)
        imageView.layer.cornerRadius = imageView.bounds.height / 2
        imageView.clipsToBounds = true
        imageView.layer.masksToBounds = true
        imageView.contentMode = .scaleAspectFill
     //   imageView.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
        navStackView.addArrangedSubview(imageView)

        navStackView.layoutIfNeeded()
    }


    navigationItem.titleView = navStackView
}

这是迄今为止的结果(虽然我不知道如何完成它):enter image description here

答案

我不确定stackView。但是对于一个简单的实现,我使用了collectionView。检查以下策略。您应该能够根据您的要求进行相应的修改。

    import UIKit


class OverlayCell: UICollectionViewCell {

    func didplay(with number: String) {
        let view = UIView(frame: CGRect(x: 0, y: 0, width: 40.0, height: 40.0))
        view.backgroundColor = UIColor.blue
        view.layer.cornerRadius = 20.0
        view.layer.masksToBounds = true

        view.layer.borderColor = UIColor.white.cgColor
        view.layer.borderWidth = 2.0

        let label = UILabel(frame: CGRect(x: 2, y: 2, width: view.bounds.width - 4, height: view.bounds.height - 4))
        label.textColor = .white
        label.text = number
        label.textAlignment = .center

        view.addSubview(label)
        contentView.addSubview(view)
        contentView.transform = CGAffineTransform(scaleX: -1, y: 1)
    }
}



class OverlayedView: UIView {

    var mainView: UIView!
    var imageCollection: UICollectionView!

    //Static for now
    let cellWidth: CGFloat = 40.0
    let cellHeight: CGFloat = 40.0
    var collectionWidth: CGFloat = 115.0

    override init(frame: CGRect) {
        super.init(frame: frame)
        loadNib()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        loadNib()
    }

    private func loadNib() {
        if let view = Bundle.main.loadNibNamed("OverlayedView", owner: self, options: nil)?.first as? UIView {
            mainView = view
            mainView.frame = self.bounds
            self.backgroundColor = .black
            addSubview(view)
        }
    }


    var dataSource = ["4","3","2","1"]

    func loadData() {

    //dynamically calculate collectionWidth to be able to kepp it in center
        collectionWidth = dataSource.count >= 4 ? CGFloat(dataSource.count) * cellWidth -  CGFloat((dataSource.count - 1) * 15) : CGFloat(dataSource.count) * cellWidth - CGFloat((dataSource.count - 1) * 15)  //CGFloat(dataSource.count * 15) here is the item spacing from delegate -15 inward so that we can get overlapping effect


        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        imageCollection = UICollectionView(frame: CGRect(x: 0, y: 0, width: collectionWidth, height: self.bounds.height), collectionViewLayout: layout)
        imageCollection.center = mainView.center

        imageCollection.register(OverlayCell.self, forCellWithReuseIdentifier: "Cell")
        //flip the collectionView so that it loads from right to left for overlapping effect
        imageCollection.transform = CGAffineTransform(scaleX: -1, y: 1)

        imageCollection.delegate = self
        imageCollection.dataSource = self

        mainView.addSubview(imageCollection)
}

 }


extension OverlayedView: UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if dataSource.count > 4 {
            return 4
        }
        return dataSource.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! OverlayCell
       cell.didplay(with: dataSource[indexPath.row])
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let size = CGSize(width: 40.0 , height: 40.0)
        return size
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0.0
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return -15.0
    }
} 

用法:

let navOverlay = OverlayedView(frame: CGRect(x: 0, y: 0, width: 250.0, height: 44.0))
    navOverlay.loadData() . //pass your data to this method
    navigationItem.titleView = navOverlay
另一答案

我终于明白了。不确定这是否是实现它的正确方法,但它是实现它的一种方式,并且效果很好。要注意的事情 - 我必须根据我们拥有的图像数量计算navStackView宽度。超过5-6张图像太过笼罩,因此,不超过5张图像。

navStackView.spacing也是根据图像之间的宽度和空间计算的。

var navStackView : UIStackView = {
    let stack = UIStackView()
    stack.axis = .horizontal
    stack.alignment = .fill
    stack.distribution = .fillEqually
    stack.translatesAutoresizingMaskIntoConstraints = false
    return stack
}()

var moreLabel: UILabel = {
    let label = UILabel()
    label.text = "+ 5 more"
    label.textColor = .black
    label.textAlignment = .left
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()

var images = ["1", "2", "3", "4", "3",  "3"]

override func viewDidLoad() {
    super.viewDidLoad()

    let navController = navigationController!

    navController.navigationBar.addSubview(navStackView)
    // x, y, w, h
    navStackView.widthAnchor.constraint(equalToConstant: 95).isActive = true
    navStackView.centerYAnchor.constraint(equalTo: navController.navigationBar.centerYAnchor).isActive = true
    navStackView.heightAnchor.constraint(equalToConstant: 35).isActive = true
    navStackView.centerXAnchor.constraint(equalTo: navController.navigationBar.centerXAnchor).isActive = true

    // image height = 35, image width = 35
    // when subtracting spacing from NavStackView, we need to subtrack from the width as well for (items - 1)

    switch images.count {
    case 0:
        print("0 images")
    case 1:
        changeNavStackWidth(constant: 60, spacing: 0)
        moreLabel.isHidden = true
    case 2:
        changeNavStackWidth(constant: 80, spacing: 10)
        moreLabel.isHidden = true
    case 3:
        changeNavStackWidth(constant: 95, spacing: -5)
        moreLabel.isHidden = true
    case 4:
        changeNavStackWidth(constant: 110, spacing: -10)
        moreLabel.isHidden = true
    case 5:
        changeNavStackWidth(constant: 95, spacing: -20)
        moreLabel.isHidden = true
    case 6...1000:
        changeNavStackWidth(constant: 95, spacing: -20)
        moreLabel.isHidden = false
    default:
        print("default")
    }


    for image in images {
        let imageView = UIImageView()
        imageView.image = UIImage(named: image)
        imageView.layer.borderColor = UIColor.white.cgColor
        imageView.layer.borderWidth = 1
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true

        navStackView.addArrangedSubview(imageView)
        navStackView.layoutIfNeeded()
    }
    navController.navigationBar.addSubview(moreLabel)

    // x, y ,w, h
    moreLabel.leadingAnchor.constraint(equalTo: navStackView.trailingAnchor, constant: 50).isActive = true
    moreLabel.topAnchor.constraint(equalTo: navStackView.topAnchor).isActive = true
    moreLabel.bottomAnchor.constraint(equalTo: navStackView.bottomAnchor).isActive = true

    navigationItem.titleView = navStackView

    let stackTap = UITapGestureRecognizer()
    stackTap.addTarget(self, action: #selector(stackTapped))
    navStackView.isUserInteractionEnabled = true
    navStackView.addGestureRecognizer(stackTap)
}

@objc func stackTapped() {
    print("tapp")
}

func changeNavStackWidth(constant: CGFloat, spacing: CGFloat) {
    navStackView.constraints.forEach { constraint in
        if constraint.firstAttribute == .width {
            constraint.constant = constant
        }
    }
    navStackView.spacing = spacing
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    navStackView.subviews.forEach { $0.layer.cornerRadius = $0.frame.height / 2 }
}

以上是关于导航栏标题中的多个叠加图像的主要内容,如果未能解决你的问题,请参考以下文章

Android 全屏片段不显示导航和状态栏后面的元素

是否可以使用 AndroidX 导航将片段中的操作栏标题居中?

叠加在内联导航栏之上

通过底部导航栏更改片段时恢复片段状态

使用底部导航栏防止片段刷新

在代码中修改导航栏会修改情节提要中的多个导航栏