Многолинейная метка внутри нескольких стековых представлений внутри UITableViewCell - PullRequest
0 голосов
/ 21 октября 2019

У меня есть иерархия представлений, как показано ниже;

UITableViewCell ->
                  -> UIView -> UIStackView (axis: vertical, distribution: fill)
                    -> UIStackView (axis: horizontal, alignment: top, distribution: fillEqually)
                     -> UIView -> UIStackView(axis:vertical, distribution: fill)
                      -> TwoLabelView

Моя проблема в том, что надписи не получают более одной строки. Я прочитал каждый вопрос в SO, а также попробовал все возможности, но ни один из них не сработал. На скриншоте ниже, в левом верхнем углу, должно быть две пары меток, но даже одна из них не отображается.

Мой вопрос заключается в том, как мне добиться многострочного в первом поле (как для левого, так и для правого)?

Если я изменю распределение представлений верхнего стека на fillProportionally, метки становятся многострочными, но между последним элементом первого блока и самим блоком будет пробел

image 1.1

Мои первые представления верхнего стека

//This is the Stackview used just below UITableViewCell
private let stackView: UIStackView = {
let s = UIStackView()
s.distribution = .fill
s.axis = .vertical
s.spacing = 10
s.translatesAutoresizingMaskIntoConstraints = false
return s
}()

//This is used to create two horizontal box next to each other
private let myStackView: UIStackView = {
let s = UIStackView()
s.distribution = .fillEqually
s.spacing = 10
s.axis = .horizontal
//s.alignment = .center
s.translatesAutoresizingMaskIntoConstraints = false
return s
}()

Класс UILabel:

fileprivate class FixAutoLabel: UILabel {

override func layoutSubviews() {
    super.layoutSubviews()
    if(self.preferredMaxLayoutWidth != self.bounds.size.width) {
        self.preferredMaxLayoutWidth = self.bounds.size.width
    }
}

}

@IBDesignable class TwoLabelView: UIView {

var topMargin: CGFloat = 0.0
var verticalSpacing: CGFloat = 3.0
var bottomMargin: CGFloat = 0.0

@IBInspectable var firstLabelText: String = "" { didSet { updateView() } }
@IBInspectable var secondLabelText: String = "" { didSet { updateView() } }

fileprivate var firstLabel: FixAutoLabel!
fileprivate var secondLabel: FixAutoLabel!

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

required public init?(coder: NSCoder) {
    super.init(coder:coder)
    setUpView()
}

override func prepareForInterfaceBuilder() {
    super.prepareForInterfaceBuilder()
    setUpView()
}

func setUpView() {

    firstLabel = FixAutoLabel()
    firstLabel.font = UIFont.systemFont(ofSize: 18.0, weight: UIFont.Weight.bold)
    firstLabel.numberOfLines = 0
    firstLabel.lineBreakMode = NSLineBreakMode.byTruncatingTail

    secondLabel = FixAutoLabel()
    secondLabel.font = UIFont.systemFont(ofSize: 13.0, weight: UIFont.Weight.regular)
    secondLabel.numberOfLines = 1
    secondLabel.lineBreakMode = NSLineBreakMode.byTruncatingTail

    addSubview(firstLabel)
    addSubview(secondLabel)

    // we're going to set the constraints
    firstLabel .translatesAutoresizingMaskIntoConstraints = false
    secondLabel.translatesAutoresizingMaskIntoConstraints = false

    // pin both labels' left-edges to left-edge of self
    firstLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 0.0).isActive = true
    secondLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 0.0).isActive = true

    // pin both labels' right-edges to right-edge of self
    firstLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: 0.0).isActive = true
    secondLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: 0.0).isActive = true

    // pin firstLabel to the top of self + topMargin (padding)
    firstLabel.topAnchor.constraint(equalTo: topAnchor, constant: topMargin).isActive = true

    // pin top of secondLabel to bottom of firstLabel + verticalSpacing
    secondLabel.topAnchor.constraint(equalTo: firstLabel.bottomAnchor, constant: verticalSpacing).isActive = true

    // pin bottom of self to bottom of secondLabel + bottomMargin (padding)
    bottomAnchor.constraint(equalTo: secondLabel.bottomAnchor, constant: bottomMargin).isActive = true

    // call common "refresh" func
    updateView()
}

func updateView() {

    firstLabel.preferredMaxLayoutWidth = self.bounds.width
    secondLabel.preferredMaxLayoutWidth = self.bounds.width

    firstLabel.text = firstLabelText
    secondLabel.text = secondLabelText

    firstLabel.sizeToFit()
    secondLabel.sizeToFit()

    setNeedsUpdateConstraints()

}

override open var intrinsicContentSize : CGSize {
    // just has to have SOME intrinsic content size defined
    // this will be overridden by the constraints
    return CGSize(width: 1, height: 1)
}
}

UIView -> Класс UIStackView

class ViewWithStack: UIView {

let verticalStackView: UIStackView = {
    let s = UIStackView()
    s.distribution = .fillEqually
    s.spacing = 10
    s.axis = .vertical
    s.translatesAutoresizingMaskIntoConstraints = false
    return s
}()

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

    self.translatesAutoresizingMaskIntoConstraints = false
    self.backgroundColor = UIColor.white
    self.layer.cornerRadius = 6.0
    self.layer.applySketchShadow(color: UIColor(red:0.56, green:0.56, blue:0.56, alpha:1), alpha: 0.2, x: 0, y: 0, blur: 10, spread: 0)

    addSubview(verticalStackView)
    let lessThan = verticalStackView.bottomAnchor.constraint(lessThanOrEqualTo: self.bottomAnchor, constant: 0)
    lessThan.priority = UILayoutPriority(1000)
    lessThan.isActive = true
    verticalStackView.leftAnchor.constraint(equalTo: self.leftAnchor,constant: 0).isActive = true
    verticalStackView.rightAnchor.constraint(equalTo: self.rightAnchor,constant: 0).isActive = true
    verticalStackView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true

    verticalStackView.layoutMargins = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)
    verticalStackView.isLayoutMarginsRelativeArrangement = true
}

convenience init(orientation: NSLayoutConstraint.Axis,labelsArray: [UIView]) {
    self.init()
    verticalStackView.axis = orientation
    for label in labelsArray {
        verticalStackView.addArrangedSubview(label)
    }
}
required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}
}

Пример класса контроллера (Это свернутая версия всего проекта):

class ViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {

@IBOutlet weak var tableView: UITableView!
let viewWithStack = BoxView()
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    tableView.delegate = self
    tableView.dataSource = self
    tableView.register(TableViewCell.self, forCellReuseIdentifier: "myCell")
    tableView.rowHeight = UITableView.automaticDimension

}

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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell: TableViewCell = tableView.dequeueReusableCell(withIdentifier: "myCell") as! TableViewCell

    if (indexPath.row == 0) {
        cell.setup(viewWithStack: self.viewWithStack)
    } else {
        cell.backgroundColor = UIColor.black
    }
    return cell
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    //return 500
    if ( indexPath.row == 0) {
        return UITableView.automaticDimension
    } else {
        return 40
    }
}

}

EDIT Я создал минимальный проект, затем обнаружил, что моя проблема в том, что мояВ проекте реализована функция heightForRow, которая переопределяет UITableViewAutomaticDimension, так что она дает неверную высоту для моего компонента. Я думаю, что я должен посмотреть, как получить размер высоты компонента? потому что я не могу удалить функцию heightForRow, которая решает мою проблему.

Пример ссылки на проект https://github.com/emreond/tableviewWithStackView/tree/master/tableViewWithStackViewEx

Пример проекта имеет амбициозные макеты при открытии отладчика представления. Я думаю, когда я их исправлю, все должно быть хорошо.

1 Ответ

3 голосов
/ 21 октября 2019

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

Лучший способ проверить это:

  • создать новый проект
  • создать новый файл с именем TestTableViewController.swift
  • скопировать и вставить приведенный ниже код в этот файл (заменить код шаблона по умолчанию)
  • добавить UITableViewController к раскадровке
  • присвоить его пользовательскому классу TestTableViewController
  • внедрить его в UINavigationController
  • установить UINavigationController какIs Initial View Controller
  • запустить приложение

Вот что вы должны увидеть в результате:

enter image description here

Я основал классы на том, что вы опубликовали (удалил ненужный код, и я предполагаю, что другие ячейки работают так, как вам нужно).

//
//  TestTableViewController.swift
//
//  Created by Don Mag on 10/21/19.
//

import UIKit

class SideBySideCell: UITableViewCell {

    let horizStackView: UIStackView = {
        let v = UIStackView()
        v.axis = .horizontal
        v.alignment = .fill
        v.distribution = .fillEqually
        v.spacing = 10
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }

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

    override func prepareForReuse() {
        horizStackView.arrangedSubviews.forEach {
            $0.removeFromSuperview()
        }
    }

    func commonInit() -> Void {

        contentView.backgroundColor = UIColor(white: 0.8, alpha: 1.0)

        contentView.addSubview(horizStackView)

        let g = contentView.layoutMarginsGuide

        NSLayoutConstraint.activate([
            horizStackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
            horizStackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
            horizStackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            horizStackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
        ])

    }

    func addViewWithStack(_ v: ViewWithStack) -> Void {
        horizStackView.addArrangedSubview(v)
    }

}

class TestTableViewController: UITableViewController {

    let sideBySideReuseID = "sbsID"

    override func viewDidLoad() {
        super.viewDidLoad()

        // register custom SideBySide cell for reuse
        tableView.register(SideBySideCell.self, forCellReuseIdentifier: sideBySideReuseID)

        tableView.separatorStyle = .none

    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

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

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

        if indexPath.row == 0 {

            let cell = tableView.dequeueReusableCell(withIdentifier: sideBySideReuseID, for: indexPath) as! SideBySideCell

            let twoLabelView1 = TwoLabelView()
            twoLabelView1.firstLabelText = "Text for first label on left-side."
            twoLabelView1.secondLabelText = "10.765,00TL"

            let twoLabelView2 = TwoLabelView()
            twoLabelView2.firstLabelText = "Text for second-first label on left-side."
            twoLabelView2.secondLabelText = "10.765,00TL"

            let twoLabelView3 = TwoLabelView()
            twoLabelView3.firstLabelText = "Text for the first label on right-side."
            twoLabelView3.secondLabelText = "10.765,00TL"

            let leftStackV = ViewWithStack(orientation: .vertical, labelsArray: [twoLabelView1, twoLabelView2])
            let rightStackV = ViewWithStack(orientation: .vertical, labelsArray: [twoLabelView3])

            cell.addViewWithStack(leftStackV)
            cell.addViewWithStack(rightStackV)

            return cell

        }

        // create ViewWithStack using just a simple label
        let cell = tableView.dequeueReusableCell(withIdentifier: sideBySideReuseID, for: indexPath) as! SideBySideCell

        let v = UILabel()
        v.text = "This is row \(indexPath.row)"
        let aStackV = ViewWithStack(orientation: .vertical, labelsArray: [v])
        cell.addViewWithStack(aStackV)

        return cell

    }

}

@IBDesignable class TwoLabelView: UIView {

    var topMargin: CGFloat = 0.0
    var verticalSpacing: CGFloat = 3.0
    var bottomMargin: CGFloat = 0.0

    @IBInspectable var firstLabelText: String = "" { didSet { updateView() } }
    @IBInspectable var secondLabelText: String = "" { didSet { updateView() } }

    fileprivate var firstLabel: UILabel = {
        let v = UILabel()
        return v
    }()

    fileprivate var secondLabel: UILabel = {
        let v = UILabel()
        return v
    }()

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

    required public init?(coder: NSCoder) {
        super.init(coder:coder)
        setUpView()
    }

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        setUpView()
    }

    func setUpView() {

        firstLabel.font = UIFont.systemFont(ofSize: 18.0, weight: UIFont.Weight.bold)
        firstLabel.numberOfLines = 0

        secondLabel.font = UIFont.systemFont(ofSize: 13.0, weight: UIFont.Weight.regular)
        secondLabel.numberOfLines = 1

        addSubview(firstLabel)
        addSubview(secondLabel)

        // we're going to set the constraints
        firstLabel .translatesAutoresizingMaskIntoConstraints = false
        secondLabel.translatesAutoresizingMaskIntoConstraints = false

        // Note: recommended to use Leading / Trailing rather than Left / Right
        NSLayoutConstraint.activate([

            // pin both labels' left-edges to left-edge of self
            firstLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
            secondLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),

            // pin both labels' right-edges to right-edge of self
            firstLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),
            secondLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),

            // pin firstLabel to the top of self + topMargin (padding)
            firstLabel.topAnchor.constraint(equalTo: topAnchor, constant: topMargin),

            // pin top of secondLabel to bottom of firstLabel + verticalSpacing
            secondLabel.topAnchor.constraint(equalTo: firstLabel.bottomAnchor, constant: verticalSpacing),

            // pin bottom of self to >= (bottom of secondLabel + bottomMargin (padding))
            bottomAnchor.constraint(greaterThanOrEqualTo: secondLabel.bottomAnchor, constant: bottomMargin),

        ])

    }

    func updateView() -> Void {
        firstLabel.text = firstLabelText
        secondLabel.text = secondLabelText
    }

}

class ViewWithStack: UIView {

    let verticalStackView: UIStackView = {
        let s = UIStackView()
        s.distribution = .fill
        s.spacing = 10
        s.axis = .vertical
        s.translatesAutoresizingMaskIntoConstraints = false
        return s
    }()

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

        self.translatesAutoresizingMaskIntoConstraints = false
        self.backgroundColor = UIColor.white
        self.layer.cornerRadius = 6.0
//      self.layer.applySketchShadow(color: UIColor(red:0.56, green:0.56, blue:0.56, alpha:1), alpha: 0.2, x: 0, y: 0, blur: 10, spread: 0)

        addSubview(verticalStackView)

        NSLayoutConstraint.activate([

            // constrain to all 4 sides
            verticalStackView.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),
            verticalStackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0),
            verticalStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
            verticalStackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),

        ])

        verticalStackView.layoutMargins = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)
        verticalStackView.isLayoutMarginsRelativeArrangement = true

    }

    convenience init(orientation: NSLayoutConstraint.Axis, labelsArray: [UIView]) {
        self.init()
        verticalStackView.axis = orientation
        for label in labelsArray {
            verticalStackView.addArrangedSubview(label)
        }
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
...