基于模板创建自定义视图
Creating custom views based off of a template
现在我已经创建了一个 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({[=10=].sectionTitle.lowercased().contains(searchBar.text!.lowercased()) || [=10=].rowTitles.map{[=10=].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()
}
}
这是一个相当基本的动态垂直示例 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
行中获取正确数据的附加问题。您需要使用它的 section
和 row
属性获取给定 IndexPath
的 Data
对象,就像在 cellForRowAt
.[=22= 中一样]
var data: Data?
if searching {
data = self.searchArray[indexPath.section].rowTitles[indexPath.row]
} else {
data = self.dataArray[indexPath.section].rowTitles[indexPath.row]
}
现在我已经创建了一个 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({[=10=].sectionTitle.lowercased().contains(searchBar.text!.lowercased()) || [=10=].rowTitles.map{[=10=].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()
}
}
这是一个相当基本的动态垂直示例 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
行中获取正确数据的附加问题。您需要使用它的 section
和 row
属性获取给定 IndexPath
的 Data
对象,就像在 cellForRowAt
.[=22= 中一样]
var data: Data?
if searching {
data = self.searchArray[indexPath.section].rowTitles[indexPath.row]
} else {
data = self.dataArray[indexPath.section].rowTitles[indexPath.row]
}