Я знаю, как центрировать отдельное изображение в UINavigationBar, но не знаю, как это сделать с динамическим числом изображений. У меня есть приложение для чата, которое поддерживает групповые чаты. Количество человек в групповом чате может быть всего 3, но нет верхнего предела.

В UINavigationBar я должен установить заголовок так, чтобы отображалось как минимум 4 или 5 наложенных изображений (но не более того, поскольку он выглядит странно в UINavigationBar) и UILabel, показывающий, как гораздо больше пользователей находятся в том же групповом чате (например, + 15 more). Название (все изображения и этикетка) должно располагаться по центру UINavigationBar. Изображения загружаются с сервера.

Когда пользователь нажимает на заголовок (любое изображение или ярлык в UINavigationBar), он должен запускать действие, чтобы отобразить полный список пользователей в отдельном UIViewController

Количество наложенных изображений динамическое (на основе каждого группового чата), но я не могу понять, как это сделать. Вот как должен выглядеть конечный результат:

enter image description here

Кто-нибудь делал это раньше или есть идея, как это сделать? Помощь очень ценится

< Сильный > UPDATE :

Я пытался добиться этого с помощью 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
}

Вот результат (я застрял, не знаю, как этого добиться): введите описание изображения здесь

0
Dani 13 Сен 2018 в 10:44

2 ответа

Лучший ответ

Я наконец понял это. Не уверен, что это правильный способ добиться этого, но это способ добиться этого, и он отлично работает. На что следует обратить внимание - мне нужно рассчитать ширину 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 }
}
0
Dani 14 Сен 2018 в 09:16

Я не уверен насчет 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
0
kathayatnk 13 Сен 2018 в 09:56