如何使用.xib文件创建Uiviews的水平滚动堆栈视图

发布于 2025-01-21 04:21:39 字数 1318 浏览 0 评论 0原文

我想使用.xib文件创建一个uiviews的水平滚动堆栈,到目前为止,我已经创建了.xib故事板以及随之而来的Cocoatouch类,并且由于这是我第一次工作,因此与之相关的Cocoatouch类也很难设置。使用.xib文件。

我希望输出看起来像这样:

但是,当我运行该应用程序时,它会引发此错误:由于未知的例外'nsunknownekyexception'终止应用程序,原因是:'[< nsObject 0x6000029f13d0> setValue:forundefinedkey:]:此类不适合键入键入的键值编码。

我将文件所有者的类设置为Whatshottile类以及视图本身。

这是Whatshottile类:

class WhatsHotTile: UIView {
    @IBOutlet weak var itemName: UILabel!
    @IBOutlet weak var itemImage: UIImageView!
}

这是我尝试创建水平滚动stackView的代码:

for shoe in Shoes {
        if shoe.trending == true {
            if let whatsHotTile = Bundle.main.loadNibNamed("WhatsHotTile", owner: nil, options: nil)!.first as? WhatsHotTile {
                whatsHotTile.itemName.text = shoe.name
                whatsHotTile.itemImage.image = shoe.image
                whatsHotStackView.addArrangedSubview(whatsHotTile)
            }
        }
    }

我尚未完成的一件事可能会或不需要完成的一件事是添加一个空白的uiview在卷轴内部的水平堆栈视图中。这是需要做的事情还是在编程上完全工作?

由于这是我第一次使用.xib文件,有人可以解释为什么发生此错误 /如何修复它以及创建.xib文件并在代码中使用它们的正确方法,如果这样,那将是非常多的话感谢。

I want to create a horizontal scrolling stackView of UIViews using .Xib files, So far I've created the .Xib storyboard as well as the cocoaTouch class that goes along with it and am having trouble setting it up since this is my first time working with .Xib files.

I want the output to look something like this:
ScrollView Desired result

But when I run the app it throws this error: Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSObject 0x6000029f13d0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key itemImage.' and I don't know why this is happening since I'm very unfamiliar with .Xib files.

I've set the file owner's class to the WhatsHotTile class as well as the view itself.

Here is the WhatsHotTile class:

class WhatsHotTile: UIView {
    @IBOutlet weak var itemName: UILabel!
    @IBOutlet weak var itemImage: UIImageView!
}

And here is the code where I attempt to create the horizontal scrolling stackView:

for shoe in Shoes {
        if shoe.trending == true {
            if let whatsHotTile = Bundle.main.loadNibNamed("WhatsHotTile", owner: nil, options: nil)!.first as? WhatsHotTile {
                whatsHotTile.itemName.text = shoe.name
                whatsHotTile.itemImage.image = shoe.image
                whatsHotStackView.addArrangedSubview(whatsHotTile)
            }
        }
    }

One thing that I haven't done that may or may not need to be done is adding a blank UIView within the horizontal stackView that's inside of the the scrollView. Is that something that needs to be done or does doing it completely programatically work?

Since this is my first time working with .Xib files could someone explain why this error is occurring / how to fix it as well as what's the proper way to create .Xib files and use them within your code, if so that would be very much appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

裂开嘴轻声笑有多痛 2025-01-28 04:21:39

问题(我相信)是只有一些可可触摸类支持.xib文件,而uiview不是其中之一。当我打开Xcode并尝试新文件时 - &gt;可可触摸类 - &gt;子类的子类:uiview,它禁用了的复选框,还可以创建.xib文件do 允许.xib文件的一些示例类是uitaiteViewuicollectionView

由于您试图水平滚动项目,而不是垂直滚动,所以我想您不需要uitaiteView。这留下了uicollectionView。这非常类似于uitaiteView,因此使用我已经知道的表观视图,以及此堆栈溢出帖子链接在此处:

Q: UICOLLECTIONVIEW:一行或列

.com/a/32584637/18248018

和a: a>

我以你描述的方式工作。这是一个简单的工作示例:

首先,创建一个新的可可触摸类文件,作为uicollectionView的子类,然后选中“也创建.xib文件”框。我将此子类命名为“ Whatshottile”,以保持与您的原始代码的亲密关系。

Whatshottile .xib故事板的属性检查器面板中,将其设置为重复使用标识符。在我的示例中,我使用了字符串tilereuseidentifier

但是,您需要设计.xib文件:要重新创建代码,我将uiimageView放在uilabel上方。

WhatShottile.swift文件将在代码中替换uiview的子类,并使用相同的两个iboutlets

import UIKit

class WhatsHotTile: UICollectionViewCell {
    @IBOutlet weak var itemName: UILabel!
    @IBOutlet weak var itemImage: UIImageView!
    
    override func awakeFromNib() {
        super.awakeFromNib()
    }
}

in main。故事板,将uicollectionView放入视图控制器中。它将已经使用一个原型单元格初始化:将该单元格的标识符(在属性检查器中)设置为您为.xib文件选择的同一标识符(在示例中,它是tilereUseIdentIdentifier

ViewController 文件,创建iboutlet uicollectionView和内部viewDidload,设置Collection View's dataSource代码> self(参考视图控制器),这是使.xib文件与您的代码一起使用的关键步骤, 用于收集视图的笔尖,使用下面的示例代码中的语法:

override func viewDidLoad() {
   super.viewDidLoad()
        
   collectionOfShoes.dataSource = self
   collectionOfShoes.register(UINib(nibName: "WhatsHotTile", bundle: nil), forCellWithReuseIdentifier: "TileReuseIdentifier")
}

然后,您必须在集合视图中实现数据源方法。在此答案的底部,代码除了两个必需的方法,还定义numberSections,然后将其设置为“鞋子”阵列的长度。代码>返回1的方法。与以下代码内部viewdidlayoutsubviews发生在viewdidload之后发生的,这将导致集合视图水平滚动而不是垂直滚动,而不是否则将:

override func viewDidLayoutSubviews() {
    // REFERENCE 1:  https://stackoverflow.com/a/32584637/18248018
    // REFERENCE 2:  https://stackoverflow.com/a/59206057/18248018
    let collectionViewFlowControl = UICollectionViewFlowLayout()
    collectionViewFlowControl.itemSize = CGSize(width: 150.0, height: 150.0)
    collectionViewFlowControl.scrollDirection = UICollectionView.ScrollDirection.horizontal
    collectionOfShoes.collectionViewLayout = collectionViewFlowControl
}

在这些步骤之后,您应该使用与原始代码没有太大不同的内容,该代码看起来与您作为目标行为提供的图像参考相似。这是view controller中的完整代码:

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var collectionOfShoes: UICollectionView!
    
    struct Shoe {
        let name: String
        let image: UIImage
    }
    
    var shoes: [Shoe] = [Shoe(name: "Shoe 1", image: UIImage(named: "shoe_1")!), Shoe(name: "Shoe 2", image: UIImage(named: "shoe_2")!), Shoe(name: "Shoe 3", image: UIImage(named: "shoe_3")!), Shoe(name: "Shoe 4", image: UIImage(named: "shoe_1")!), Shoe(name: "Shoe 5", image: UIImage(named: "shoe_2")!), Shoe(name: "Shoe 6", image: UIImage(named: "shoe_3")!), Shoe(name: "Shoe 7", image: UIImage(named: "shoe_1")!), Shoe(name: "Shoe 8", image: UIImage(named: "shoe_2")!), Shoe(name: "Shoe 9", image: UIImage(named: "shoe_3")!)]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        collectionOfShoes.dataSource = self
        collectionOfShoes.register(UINib(nibName: "WhatsHotTile", bundle: nil), forCellWithReuseIdentifier: "TileReuseIdentifier")
    }
    
    override func viewDidLayoutSubviews() {
        // REFERENCE 1:  https://stackoverflow.com/a/32584637/18248018
        // REFERENCE 2:  https://stackoverflow.com/a/59206057/18248018
        let collectionViewFlowControl = UICollectionViewFlowLayout()
        collectionViewFlowControl.itemSize = CGSize(width: 150.0, height: 150.0)
        collectionViewFlowControl.scrollDirection = UICollectionView.ScrollDirection.horizontal
        collectionOfShoes.collectionViewLayout = collectionViewFlowControl
    }
}

extension ViewController: UICollectionViewDataSource {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return shoes.count
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let whatsHotTile = collectionView.dequeueReusableCell(withReuseIdentifier: "TileReuseIdentifier", for: indexPath) as! WhatsHotTile
        whatsHotTile.itemName.text = shoes[indexPath.section].name
        whatsHotTile.itemImage.image = shoes[indexPath.section].image
        return whatsHotTile
    }
}

功能应用的图片:

“在模拟器中运行的集合视图的图片水平滚动”

信用属于William T.,Matt和Ilker Baltaci我提到的问题和答案是实现水平滚动功能。

The issue (I believe) is that only some Cocoa Touch classes support .xib files, and UIView is not one of those. When I opened up Xcode and tried New File -> Cocoa Touch Class -> Subclass of: UIView, it disabled the checkbox for also create .xib file. Some example classes that do allow .xib files are the UITableView and UICollectionView.

Since you're trying to scroll items horizontally, not vertically, I'd guess you don't want a UITableView. This leaves the UICollectionView. This works a lot like the UITableView, so using what I already knew about table views, plus this Stack Overflow post linked here:

Q: UICollectionView: One Row or Column

A: https://stackoverflow.com/a/32584637/18248018

and A: https://stackoverflow.com/a/59206057/18248018

I got it to work the way you described. Here is a simple working example:

First, create a new Cocoa Touch class file as a subclass of UICollectionView, and check the 'also create .xib file' box. I named this subclass 'WhatsHotTile', to stay close to your original code.

In the Attributes Inspector panel of the WhatsHotTile .xib storyboard, set it a Reuse Identifier. In my example, I used the string TileReuseIdentifier.

Design the .xib file however you want: to recreate your code, I put a UIImageView above a UILabel.

The WhatsHotTile.swift file will replace what was a subclass of UIView in your code, with the same two IBOutlets:

import UIKit

class WhatsHotTile: UICollectionViewCell {
    @IBOutlet weak var itemName: UILabel!
    @IBOutlet weak var itemImage: UIImageView!
    
    override func awakeFromNib() {
        super.awakeFromNib()
    }
}

In Main.storyboard, place a UICollectionView inside your view controller. It will come already initialized with a prototype cell: set this cell's identifier (in the Attributes Inspector) to the same identifier you chose for the .xib file (in the example, it's TileReuseIdentifier.

In the ViewController file, create an IBOutlet for the UICollectionView, and inside viewDidLoad, set the collection view's dataSource to self (referring to the view controller). Then, and this is the key step to get the .xib files to work with your code, register the nib for the collection view, using the syntax in the example code below:

override func viewDidLoad() {
   super.viewDidLoad()
        
   collectionOfShoes.dataSource = self
   collectionOfShoes.register(UINib(nibName: "WhatsHotTile", bundle: nil), forCellWithReuseIdentifier: "TileReuseIdentifier")
}

Then you'll have to implement the data source methods for the collection view. I usually do this in an extension (included in the complete view controller code at the bottom of this answer). In addition to the two required methods, also define numberOfSections and set it to the length of your 'shoes' array. Then, set the numberOfItemsInSection method to return 1. In conjunction with the following code inside viewDidLayoutSubviews, which happens after viewDidLoad, this will cause the collection view to scroll horizontally instead of vertically as it otherwise would:

override func viewDidLayoutSubviews() {
    // REFERENCE 1:  https://stackoverflow.com/a/32584637/18248018
    // REFERENCE 2:  https://stackoverflow.com/a/59206057/18248018
    let collectionViewFlowControl = UICollectionViewFlowLayout()
    collectionViewFlowControl.itemSize = CGSize(width: 150.0, height: 150.0)
    collectionViewFlowControl.scrollDirection = UICollectionView.ScrollDirection.horizontal
    collectionOfShoes.collectionViewLayout = collectionViewFlowControl
}

After these steps, you should wind up with something not too different from your original code, which looks similar to the image reference you provided as your target behavior. Here is the complete code inside ViewController:

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var collectionOfShoes: UICollectionView!
    
    struct Shoe {
        let name: String
        let image: UIImage
    }
    
    var shoes: [Shoe] = [Shoe(name: "Shoe 1", image: UIImage(named: "shoe_1")!), Shoe(name: "Shoe 2", image: UIImage(named: "shoe_2")!), Shoe(name: "Shoe 3", image: UIImage(named: "shoe_3")!), Shoe(name: "Shoe 4", image: UIImage(named: "shoe_1")!), Shoe(name: "Shoe 5", image: UIImage(named: "shoe_2")!), Shoe(name: "Shoe 6", image: UIImage(named: "shoe_3")!), Shoe(name: "Shoe 7", image: UIImage(named: "shoe_1")!), Shoe(name: "Shoe 8", image: UIImage(named: "shoe_2")!), Shoe(name: "Shoe 9", image: UIImage(named: "shoe_3")!)]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        collectionOfShoes.dataSource = self
        collectionOfShoes.register(UINib(nibName: "WhatsHotTile", bundle: nil), forCellWithReuseIdentifier: "TileReuseIdentifier")
    }
    
    override func viewDidLayoutSubviews() {
        // REFERENCE 1:  https://stackoverflow.com/a/32584637/18248018
        // REFERENCE 2:  https://stackoverflow.com/a/59206057/18248018
        let collectionViewFlowControl = UICollectionViewFlowLayout()
        collectionViewFlowControl.itemSize = CGSize(width: 150.0, height: 150.0)
        collectionViewFlowControl.scrollDirection = UICollectionView.ScrollDirection.horizontal
        collectionOfShoes.collectionViewLayout = collectionViewFlowControl
    }
}

extension ViewController: UICollectionViewDataSource {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return shoes.count
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let whatsHotTile = collectionView.dequeueReusableCell(withReuseIdentifier: "TileReuseIdentifier", for: indexPath) as! WhatsHotTile
        whatsHotTile.itemName.text = shoes[indexPath.section].name
        whatsHotTile.itemImage.image = shoes[indexPath.section].image
        return whatsHotTile
    }
}

A picture of the functioning app:

picture of the collection view running in the simulator scrolled horizontally

Credit belongs to William T., matt and Ilker Baltaci for the question and answers I referenced to implement the horizontal scrolling functionality.

北斗星光 2025-01-28 04:21:39

我建议在此处使用UicollectionView,而不是UistackView,因为它支持动态数据并默认滚动。

I recommend using UICollectionView here instead of UIStackView as it supports dynamic data and scrolling by default.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文