Swift 中一个垂直 ScrollView 中的多个水平 ScrollView?
Posted
技术标签:
【中文标题】Swift 中一个垂直 ScrollView 中的多个水平 ScrollView?【英文标题】:Multiple Horizontal ScrollViews In One Vertical ScrollView in Swift? 【发布时间】:2021-01-28 11:54:54 【问题描述】:我正在尝试在 Swift 中实现应用程序中最标准的布局之一。
这基本上是在一个垂直滚动视图中有多个水平滚动视图。
这些子水平滚动视图中的每一个都将包含一些带有图像的视图。
类似这样的:
实现这一目标的最佳方法是什么?
附:我需要使用代码来执行此操作,因为内容是通过远程 JSON 文件提取的。
任何指针将不胜感激。
【问题讨论】:
检查下面的答案它可能对你有帮助***.com/a/65937540/8079610 添加了可供测试的代码。 【参考方案1】:我会做以下事情。
-
为垂直滚动视图使用 UITableView。
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
-
为水平滚动视图使用 UICollectionView。
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")
-
每个 UITableViewCell 都包含一个 UICollectionView(水平滚动视图)。
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")
-
使用 UITableViewHeaderFooterView (
tableView(_:viewForHeaderInSection:)
) 作为水平滚动视图的标题
class TableViewHeader: UITableViewHeaderFooterView
static let identifier = "TableViewHeader"
我添加了一个完整的工作示例的代码。只需使用 TableViewController
即可。
更新
UITableViewCells 应该有动态的单元格高度。
经过测试,我发现最好使用固定单元格大小而不是动态单元格,因为单元格可能有不同的高度应该使用UITableView.automaticDimension
【讨论】:
【参考方案2】:使用 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。
image of compiled code
【讨论】:
兄弟,他在 swift 中而不是在 swiftUI 中提出问题 @Wings 这两个不是对立的或不同的语言。您可以使用相同的组件在故事板或使用 UIKit 的代码中复制它。这样写更容易 我可以在 UIHostingController 中使用它吗? 如果您打算使用 SwiftUI 或将其集成到 UIKit 中,这是一个很好的解决方案。请记住,SwiftUI 与 ios 13+ 兼容。 @mahan,是的,我更倾向于在我的 UIKit 项目中使用 SwiftUI,因为 SwiftUI 似乎比 UIKit 更容易实现。【参考方案3】:为主 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() 方法中。
-
您要做的最后一件事就是只需在特定单元格中将子 ViewController 添加为 view。
您可以查看以下示例作为参考:-
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 中显示特定的单元格数据等等。
【讨论】:
@user2056633....是的,请再次检查我的答案;) @roozfar....你能展示一下你在做什么的代码吗? @Wings,我的代码与您的示例相同。let firstVC = FirstViewController()
不应该是let firstVC = UIViewController()
吗?
好吧,你做错了伙伴......只需创建另一个 ViewController,其名称为 UIViewController() 的 FirstViewController 子类
在性能方面,如果您为每个水平滚动视图创建一个 UIViewController,这是一个糟糕的解决方案。以上是关于Swift 中一个垂直 ScrollView 中的多个水平 ScrollView?的主要内容,如果未能解决你的问题,请参考以下文章
UITableViewCell中的Swift ScrollView不滚动
UIScrollview中的UIWebview向scrollview发送垂直滚动事件