基于模板创建自定义视图

Posted

技术标签:

【中文标题】基于模板创建自定义视图【英文标题】:Creating custom views based off of a template 【发布时间】:2020-05-21 22:51:29 【问题描述】:

现在我创建了一个 UIStackView,其中填充了不同的通用标签和图像。当用户从我的 tableView 中选择一行时,会显示此 UIStackView。我想拥有它,以便可以根据在我的 tableView 中选择的行来更改 UIStackView。

示例:用户选择第 1 部分的第 1 行,并被带到一个屏幕,该屏幕显示了我的 UIStackView 中 tableview 的行所独有的信息。

我该怎么做呢?

这是显示我的 tableView 的代码。

import UIKit

struct Data 
   var sectionTitle = String()
   var rowTitles = [String]()


class ViewController: UIViewController 

@IBOutlet weak var searchBar: UISearchBar!
@IBOutlet weak var tableView: UITableView!

var dataArray = [Data(sectionTitle: "section 1", rowTitles: ["row 1", "row 2", "row 3"]),
    Data(sectionTitle: "section 2", rowTitles: ["row 1"]),
    Data(sectionTitle: "section 3", rowTitles: ["row 1", "row 2"])
]
var searchArray = [Data]()
var searching = false

override func viewDidLoad() 
    super.viewDidLoad()

    let labelValueImages: LabelValueImagePairs = [
    (image: image, label: "row 1", value: "This is data."),
    (image: image, label: "row 2", value: "This is data.")
]

    let myStackView = MyStackView(labelsAndValues: labelValueImages)
    view.addSubview(myStackView)
    myStackView.translatesAutoresizingMaskIntoConstraints = false
    myStackView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
    myStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
    


    tableView.delegate = self
    tableView.dataSource = self




extension ViewController: UITableViewDelegate, UITableViewDataSource 
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    if searching 
        return searchArray[section].rowTitles.count
     else 
        return dataArray[section].rowTitles.count
    

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let cell = tableView.dequeueReusableCell(withIdentifier: "searchCell")
    cell?.textLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
    cell?.textLabel?.numberOfLines = 3
    if searching 
        cell?.textLabel?.text = self.searchArray[indexPath.section].rowTitles[indexPath.row]
     else 
        cell?.textLabel?.text = self.dataArray[indexPath.section].rowTitles[indexPath.row]
    
    return cell!

func numberOfSections(in tableView: UITableView) -> Int 
    if searching 
        return searchArray.count
     else 
        return dataArray.count
    


func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
    return dataArray[section].sectionTitle


func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
    var data: Data?
if searching 
    data = self.searchArray[indexPath.section].rowTitles[indexPath.row]
 else 
    data = self.dataArray[indexPath.section].rowTitles[indexPath.row]

    let destVC = SecondViewController(data: data)
    navigationController?.pushViewController(destVC, animated: true)




extension ViewController: UISearchBarDelegate 
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) 
    if searchBar.text == nil || searchBar.text == "" 
        searching = false
        view.endEditing(true)
        tableView.reloadData()
     else 
        searching = true
        searchArray = dataArray.filter($0.sectionTitle.lowercased().contains(searchBar.text!.lowercased()) || $0.rowTitles.map$0.lowercased().contains(searchBar.text!.lowercased()))
        tableView.reloadData()
    


这是新 viewController 和 UIStackView 的代码。

import UIKit

class SecondViewController: UIViewController 

var data: Data!
var dataView: DataView  return self.view as! DataView

init(data: Data) 
    self.data = data
    super.init(nibName: nil, bundle: nil)


required init?(coder: NSCoder) 
    fatalError("init(coder:) has not been implemented")


override func viewDidLoad() 
    super.viewDidLoad()



override func loadView() 
    self.view = MyStackView(frame: UIScreen.main.bounds)
    


typealias LabelValueImagePairs = [(image: UIImage?, label: String, value: String?]

class MyStackView: UIStackView 

var labelsAndValues: LabelValueImagePairs?
func setupViews() 

    distribution = .fill
    alignment = .fill
    axis = .vertical

    labelsAndValues?.forEach( (labelValuePair) in

        let labelLabel = UILabel()
        labelLabel.translatesAutoresizingMaskIntoConstraints = false
        labelLabel.font = UIFont.systemFont(ofSize: 18, weight: .semibold)
        labelLabel.textAlignment = .center
        labelLabel.text = labelValuePair.label
        labelLabel.heightAnchor.constraint(equalToConstant: 35).isActive = true
        addArrangedSubview(labelLabel)

        if let value = labelValuePair.value 
            let valueLabel = UILabel()
            valueLabel.translatesAutoresizingMaskIntoConstraints = false
            valueLabel.font = UIFont.systemFont(ofSize: 16, weight: .light)
            valueLabel.textAlignment = .center
            valueLabel.text = value
            valueLabel.numberOfLines = 0
            valueLabel.heightAnchor.constraint(equalToConstant: 35).isActive = true
            addArrangedSubview(valueLabel)
        

        if let image = labelValuePair.image 
            let imageView = UIImageView(image: image)
            imageView.translatesAutoresizingMaskIntoConstraints = false
            imageView.contentMode = .scaleAspectFit
            imageView.heightAnchor.constraint(equalToConstant: image.size.height).isActive = true
            addArrangedSubview(imageView)
        

        let spacer = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 0, height: 25)))
        addArrangedSubview(spacer)

    )



convenience init(labelsAndValues: LabelValueImagePairs) 
    self.init()
    self.labelsAndValues = labelsAndValues
    setupViews()


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


required init(coder: NSCoder) 
    super.init(coder: coder)
    setupViews()



【问题讨论】:

您在哪一部分遇到了问题? 更改堆栈视图的值,对应于他们尊重的行。 【参考方案1】:

这是一个相当基本的动态垂直UIStackView 示例。堆栈视图接受一个元组数组,每个元组都有一个标签、可选值和可选图像。堆栈视图将从传入的数组创建适当的视图并将它们添加为排列的子视图。此示例仅在控制器的viewDidLoad 中显示堆栈视图,但它可以放置在您能够显示视图的任何位置。有任何问题,请告诉我。

堆栈视图:

typealias LabelValueImagePairs = [(label:String, value: String?, image: UIImage?)]

class MyStackView: UIStackView 

    var labelsAndValues: LabelValueImagePairs?
    func setupViews() 

        distribution = .fill
        alignment = .fill
        axis = .vertical

        labelsAndValues?.forEach( (labelValuePair) in

            let labelLabel = UILabel()
            labelLabel.translatesAutoresizingMaskIntoConstraints = false
            labelLabel.font = UIFont.systemFont(ofSize: 18, weight: .semibold)
            labelLabel.textAlignment = .center
            labelLabel.text = labelValuePair.label
            labelLabel.heightAnchor.constraint(equalToConstant: 35).isActive = true
            addArrangedSubview(labelLabel)

            if let value = labelValuePair.value 
                let valueLabel = UILabel()
                valueLabel.translatesAutoresizingMaskIntoConstraints = false
                valueLabel.font = UIFont.systemFont(ofSize: 16, weight: .light)
                valueLabel.textAlignment = .center
                valueLabel.text = value
                valueLabel.numberOfLines = 0
                valueLabel.heightAnchor.constraint(equalToConstant: 35).isActive = true
                addArrangedSubview(valueLabel)
            

            if let image = labelValuePair.image 
                let imageView = UIImageView(image: image)
                imageView.translatesAutoresizingMaskIntoConstraints = false
                imageView.contentMode = .scaleAspectFit
                imageView.heightAnchor.constraint(equalToConstant: image.size.height).isActive = true
                addArrangedSubview(imageView)
            

            let spacer = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 0, height: 25)))
            addArrangedSubview(spacer)

        )

    

    convenience init(labelsAndValues: LabelValueImagePairs) 
        self.init()
        self.labelsAndValues = labelsAndValues
        setupViews()
    

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

    required init(coder: NSCoder) 
        super.init(coder: coder)
        setupViews()
    


用法:

class ViewController: UIViewController 

    override func viewDidLoad() 
        let labelValueImages: LabelValueImagePairs = [
            (label: "First Name:", value: "John", image: nil),
            (label: "Last Name:", value: "Doe", image: nil),
            (label: "Age:", value: "25", image: nil),
            (label: "Avatar:", value: nil, image: UIImage(systemName: "person.crop.circle", withConfiguration: UIImage.SymbolConfiguration(scale: .large)))
        ]

        let myStackView = MyStackView(labelsAndValues: labelValueImages)
        view.addSubview(myStackView)
        myStackView.translatesAutoresizingMaskIntoConstraints = false
        myStackView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
        myStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
    


编辑:

关于如何在didSelect 行中获取正确数据的其他问题。您需要使用sectionrow 属性为给定的IndexPath 获取Data 对象,方法与在cellForRowAt 中的操作相同。

var data: Data?

if searching 
    data = self.searchArray[indexPath.section].rowTitles[indexPath.row]
 else 
    data = self.dataArray[indexPath.section].rowTitles[indexPath.row]

【讨论】:

谢谢。我将如何使用这些信息,以便在选择 tableview 单元格时显示这些新的堆栈视图? 在 tableView 的 didSelectRow 委托方法中推送或呈现视图控制器。 更新了答案 在您的SecondViewController 中,您使用的是硬编码值,而不是您传递给控制器​​的data。尝试设置断点或打印数据,您看起来在 didSelectRow 中正确传递了它 var data: Data? 更改为var data: String? 将为您获取单元格的值,但您期望得到什么?您的下一个控制器使用 Data 对象进行初始化,但您的 Data 数组与表格部分而不是每个单独的单元格相关。

以上是关于基于模板创建自定义视图的主要内容,如果未能解决你的问题,请参考以下文章

如何使脚手架模板添加新的自定义视图

来自 Interface Builder 的自定义 UIView

Django在使用默认视图时将自定义变量传递给模板

带有自定义模板暴露过滤器的 Drupal 7 视图不显示

Django-----视图层,模板层

在 ASP.NET MVC 3 中创建基于平台显示不同视图的自定义 ViewResult