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

时间:2018-09-13 07:44:44

标签: swift uiimageview uinavigationbar uistackview

我知道如何将UINavigationBar中的单个图像居中,但不知道如何使用动态数量的图像来做到这一点。我有一个支持群聊的聊天应用。群聊中的人数可以少至3,但没有上限。

UINavigationBar中,我必须设置标题以显示至少4或5个重叠的图像(但不能超过此数目,因为在UINavigationBar中看起来很奇怪)和{{1} }显示在同一群聊中有多少用户(即UILabel)。标题(所有图像和标签)应位于+ 15 more的中心。正在从服务器下载图像。

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

叠加图像的数量是动态的(基于每次群聊),但是我不知道该怎么做。这是最终结果的图像:

enter image description here

有人曾做过此事还是有一个如何实现此目的的想法?非常感谢帮助

更新:

我尝试使用UIViewController完成此操作,但是我遇到了多个问题。这是代码:

UIStackView

到目前为止,这是结果(尽管我很困惑,不确定如何实现):     enter image description here

2 个答案:

答案 0 :(得分:0)

我不确定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

答案 1 :(得分:0)

我终于知道了。不确定这是否是实现它的正确方法,但是这是实现它的方法,并且效果很好。注意事项-我必须根据我们拥有的图像数来计算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 }
}