带有多个部分的水平滚动的 collectionview
collectionview with the horizontal scroll with mulitple section
我想像这样开发屏幕(objective C) :
其中有部分名称:
- 我们喜爱的新游戏
- 我们喜爱的新应用程序
两者都是水平滚动,相互独立。
我的问题是我应该使用什么方式从以下两个选项中实现它。如果有任何参考样本,请提供给我:
- 我能否通过单个
UICollectionView
和不同的部分(动态)实现相同的行为。但是不同部分的滚动应该是独立的。因为第 1 部分可能有不同数量的项目(行)而第 2 部分可能有不同数量的项目(行)
- 我是否必须以编程方式获取多个集合视图
然后插入 uiscrollview(垂直滚动视图)。
abd 然后定义水平滚动并通过标记 collectionview 在单元格中添加项目。
我目前已经通过以下代码完成了水平滚动的collectionview:
- (void)viewDidLoad {
[super viewDidLoad];
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
_collectionView=[[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];
[_collectionView setDataSource:self];
[_collectionView setDelegate:self];
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cellIdentifier"];
[_collectionView setBackgroundColor:[UIColor redColor]];
[self.view addSubview:_collectionView];
// Do any additional setup after loading the view, typically from a nib.
}
#pragma mark Collection View Methods
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 15;
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];
cell.backgroundColor=[UIColor greenColor];
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(50, 50);
}
请帮忙。
你想做的事情并不难。我已经创建了您正在查看的原型。这是您的 storyboard's view controller
及其文档大纲的样子:
这是每个组件的代码
TableViewController
class TableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 5
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath) as! MyTableViewCell
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 160
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return (section%2 == 0) ? "Games we love" : "Apps we love"
}
}
MyTableViewCell
class MyTableViewCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
@IBOutlet weak var collectionView: UICollectionView!
let imageNames = ["candy_crush", "cut_the_ropes", "game_1", "little_pet_shop", "zuba"]
let gameNames = ["Candy Crush", "Cut the Ropes", "Arbitrary Game 1", "Littlest Pet Shop", "Zuba"]
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageNames.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath) as! MyCollectionViewCell
cell.imageView.image = UIImage.init(named: imageNames[indexPath.row])
cell.titleLabel.text = gameNames[indexPath.row]
cell.detailLabel.text = "Games"
return cell
}
}
MyCollectionViewCell
class MyCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var detailLabel: UILabel!
}
这是模拟器上的样子
解决方法(将 UICollectionView 与 Swift 4 结合使用)
我遇到了同样的问题,但找不到使它与 UICollectionViewFlowLayout
一起使用的方法。
如果你想使用UICollectionView
,但是SupplementaryView不够灵活,你可以这样做:
我有一个固定的 UILabel
在 CollectionView
的 top-left 一侧,还有一个浮动的 UILabel
会相应地向右移动。如果只有一个部分可见,第二个标签将被隐藏
我们可以在collectionView:cellForItemAt indexPath:
方法中拦截所有created/dequeued的单元格,但是会给你很多单元格框架(其中许多正在准备但仍在屏幕外,你将不得不管理哪个框架对应于哪个部分。如果水平滚动集合视图中有多行,这可能会很棘手
你需要截取sections和cell frame来找到最低的x坐标,最可靠的方法是检查collectionView的.visibleCells
属性。这可能需要很多次迭代(取决于可见单元格的数量),我很想找到一种更有效的方法。 (一种委托方法,一种 CollectionViewCellsDidAppear 通知,或者能够将观察者添加到 .visibleCells 属性(不可能,因为它是 read-only)
到目前为止这可行 (Swift 4),但还有改进的余地:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CellID", for: indexPath) as! GalleryCell
cell.label?.text = String((itemsArrays[indexPath.section][indexPath.row]))
var pointsDict = Dictionary<Int, CGFloat>()
for item in collectionView.visibleCells {
let point = item.convert(CGPoint.zero, to: self.superview)
let section = collectionView.indexPath(for: item)!.section
if pointsDict[section] != nil {
if point.x < pointsDict[section]! {
pointsDict[section] = point.x
}
} else {
pointsDict[section] = point.x
}
}
updateLabels(dict: pointsDict)
return cell
}
此代码将为您提供一个字典,其中包含可见部分(键)和每个部分的最低 x 坐标(值)以对齐浮动 label2(我的 label1 在 collectionView 的左侧有一个固定的 x)。您还可以动画化,hide/show 相应的标签
此解决方法不是最优的,如果使用 UICollectionViewFlowLayout
的解决方案,我将 post 提供更多信息
我想像这样开发屏幕(objective C) :
其中有部分名称:
- 我们喜爱的新游戏
- 我们喜爱的新应用程序
两者都是水平滚动,相互独立。
我的问题是我应该使用什么方式从以下两个选项中实现它。如果有任何参考样本,请提供给我:
- 我能否通过单个
UICollectionView
和不同的部分(动态)实现相同的行为。但是不同部分的滚动应该是独立的。因为第 1 部分可能有不同数量的项目(行)而第 2 部分可能有不同数量的项目(行) - 我是否必须以编程方式获取多个集合视图 然后插入 uiscrollview(垂直滚动视图)。 abd 然后定义水平滚动并通过标记 collectionview 在单元格中添加项目。
我目前已经通过以下代码完成了水平滚动的collectionview:
- (void)viewDidLoad {
[super viewDidLoad];
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
_collectionView=[[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];
[_collectionView setDataSource:self];
[_collectionView setDelegate:self];
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cellIdentifier"];
[_collectionView setBackgroundColor:[UIColor redColor]];
[self.view addSubview:_collectionView];
// Do any additional setup after loading the view, typically from a nib.
}
#pragma mark Collection View Methods
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 15;
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];
cell.backgroundColor=[UIColor greenColor];
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(50, 50);
}
请帮忙。
你想做的事情并不难。我已经创建了您正在查看的原型。这是您的 storyboard's view controller
及其文档大纲的样子:
这是每个组件的代码
TableViewController
class TableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 5
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath) as! MyTableViewCell
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 160
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return (section%2 == 0) ? "Games we love" : "Apps we love"
}
}
MyTableViewCell
class MyTableViewCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
@IBOutlet weak var collectionView: UICollectionView!
let imageNames = ["candy_crush", "cut_the_ropes", "game_1", "little_pet_shop", "zuba"]
let gameNames = ["Candy Crush", "Cut the Ropes", "Arbitrary Game 1", "Littlest Pet Shop", "Zuba"]
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageNames.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath) as! MyCollectionViewCell
cell.imageView.image = UIImage.init(named: imageNames[indexPath.row])
cell.titleLabel.text = gameNames[indexPath.row]
cell.detailLabel.text = "Games"
return cell
}
}
MyCollectionViewCell
class MyCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var detailLabel: UILabel!
}
这是模拟器上的样子
解决方法(将 UICollectionView 与 Swift 4 结合使用)
我遇到了同样的问题,但找不到使它与 UICollectionViewFlowLayout
一起使用的方法。
如果你想使用UICollectionView
,但是SupplementaryView不够灵活,你可以这样做:
我有一个固定的 UILabel
在 CollectionView
的 top-left 一侧,还有一个浮动的 UILabel
会相应地向右移动。如果只有一个部分可见,第二个标签将被隐藏
我们可以在collectionView:cellForItemAt indexPath:
方法中拦截所有created/dequeued的单元格,但是会给你很多单元格框架(其中许多正在准备但仍在屏幕外,你将不得不管理哪个框架对应于哪个部分。如果水平滚动集合视图中有多行,这可能会很棘手
你需要截取sections和cell frame来找到最低的x坐标,最可靠的方法是检查collectionView的.visibleCells
属性。这可能需要很多次迭代(取决于可见单元格的数量),我很想找到一种更有效的方法。 (一种委托方法,一种 CollectionViewCellsDidAppear 通知,或者能够将观察者添加到 .visibleCells 属性(不可能,因为它是 read-only)
到目前为止这可行 (Swift 4),但还有改进的余地:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CellID", for: indexPath) as! GalleryCell
cell.label?.text = String((itemsArrays[indexPath.section][indexPath.row]))
var pointsDict = Dictionary<Int, CGFloat>()
for item in collectionView.visibleCells {
let point = item.convert(CGPoint.zero, to: self.superview)
let section = collectionView.indexPath(for: item)!.section
if pointsDict[section] != nil {
if point.x < pointsDict[section]! {
pointsDict[section] = point.x
}
} else {
pointsDict[section] = point.x
}
}
updateLabels(dict: pointsDict)
return cell
}
此代码将为您提供一个字典,其中包含可见部分(键)和每个部分的最低 x 坐标(值)以对齐浮动 label2(我的 label1 在 collectionView 的左侧有一个固定的 x)。您还可以动画化,hide/show 相应的标签
此解决方法不是最优的,如果使用 UICollectionViewFlowLayout