我正在尝试实现 Swift 应用中最标准的布局之一。
基本上在一个垂直滚动视图中有多个水平滚动视图。
这些子水平滚动视图中的每一个都将包含一些带有图像的视图。
像这样:
实现这一目标的最佳方法是什么?
附言我需要使用代码来执行此操作,因为内容是通过远程 JSON 文件提取的。
任何指针将不胜感激。
答案 0 :(得分:6)
我会做以下事情。
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(TableViewCell.self, forCellReuseIdentifier: TableViewCell.identifier)
self.tableView.register(TableViewHeader.self, forHeaderFooterViewReuseIdentifier: TableViewHeader.identifier)
self.tableView.dataSource = self
self.tableView.delegate = self
}
}
extension TableViewController {
override func numberOfSections(in tableView: UITableView) -> Int {
return 10
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.identifier,
for: indexPath) as! TableViewCell
return cell
}
}
extension TableViewController {
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 250
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: TableViewHeader.identifier)
header?.textLabel?.text = "Header \(section)"
return header
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}
}
class CollectionView: UICollectionView {
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
self.backgroundColor = .white
self.register(CollectionViewCell.self, forCellWithReuseIdentifier: CollectionViewCell.identifier)
self.dataSource = self
self.delegate = self
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension CollectionView: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.identifier,
for: indexPath) as! CollectionViewCell
cell.index = indexPath.row
return cell
}
}
extension CollectionView: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 200, height: 250)
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 20
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
class CollectionViewCell: UICollectionViewCell {
static let identifier = "CollectionViewCell"
var index: Int? {
didSet {
if let index = index {
label.text = "\(index)"
}
}
}
private let label: UILabel = {
let label = UILabel()
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.contentView.backgroundColor = .red
self.contentView.addSubview(label)
let constraints = [
label.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor)
]
NSLayoutConstraint.activate(constraints)
label.translatesAutoresizingMaskIntoConstraints = false
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class TableViewCell: UITableViewCell {
static let identifier = "TableViewCell"
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = .white
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let subView = CollectionView(frame: .zero, collectionViewLayout: layout)
self.contentView.addSubview(subView)
let constraints = [
subView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 1),
subView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 1),
subView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 1),
subView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 1)
]
NSLayoutConstraint.activate(constraints)
subView.translatesAutoresizingMaskIntoConstraints = false
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
tableView(_:viewForHeaderInSection:)
) 作为水平滚动视图的标题
class TableViewHeader: UITableViewHeaderFooterView {
static let identifier = "TableViewHeader"
}
我添加了一个完整的工作示例的代码。只需使用 TableViewController
即可。
UITableViewCells 应该具有动态单元格高度。
经过测试,我发现最好使用固定单元格大小而不是动态单元格,因为单元格可能具有不同的高度应该使用 UITableView.automaticDimension
答案 1 :(得分:3)
使用 SwiftUI 非常简单。 您应该在 ScrollView 中使用 VStack 作为垂直视图; 以及 ScrollView 中的 HStack 用于水平视图。
这是一个例子:
struct ContentView: View {
var body: some View {
ScrollView{
ForEach(0..<10) {_ in
VStack{
ScrollView(.horizontal){
HStack(spacing: 20) {
ForEach(0..<10) {
Text("Item \($0)")
.font(.headline)
.frame(width: 100, height: 100)
.background(Color.gray)
}
}
}
}
}
}
}
}
我制作了一个 ForEach
来复制每个堆栈中的示例项目,但您应该将它们替换为您的自定义视图或内容。在您上传的图片中,每个项目都是一个带有图像和文本的 ZStack。
答案 2 :(得分:2)
最好的方法是实现这一目标:-
为主 ViewController 创建一个 UITableView。
您必须在其中创建的视图使其成为独立的 ViewController。
For ex:- for mental fitness - Create separate mental fitness ViewController for that
for sleep stories - Create separate Sleep Stories ViewController
现在高潮来了,叫做addChild()方法。
访问主 ViewController 类中的所有 ViewController,并将它们添加到 addChild() 方法内的 viewDidLoad() 方法中。
作为参考,您可以查看这些示例:-
https://www.hackingwithswift.com/example-code/uikit/how-to-use-view-controller-containment
https://www.swiftbysundell.com/basics/child-view-controllers/
优势:-
例如:-
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
//subViewControllers
let firstVC = FirstViewController()
let secondVC = SecondViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.addChild(firstVC) //adding childVC here
self.addChild(secondVC)
}
}
UITableViewDataSource 和委托方法
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 250
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? MainCell else {
return UITableViewCell()
}
if indexPath.row == 0 {
cell.contentView.addSubview(firstVC.view)
}
if indexPath.row == 1 {
cell.contentView.addSubview(secondVC.view)
}
return cell
}
}
UITableViewCell 类
class MainCell: UITableViewCell {
}
这样您就可以轻松管理来自服务器的数据。因为它会给你一个优势,在单独的 ViewController 中显示特定的单元格数据等等。