【问题标题】:Load view from XIB as a subview of a scrollview从 XIB 加载视图作为滚动视图的子视图
【发布时间】:2019-07-04 19:25:04
【问题描述】:

我仍然是 SO 和 Swift 新手,所以请耐心等待并随意跳过这个问题 :-)

XIBawakeFromNib 的主体中,我想加载一些视图作为UIScrollView 的子视图(基本上,XIB 包含一个滚动视图、一个标签和一个按钮)。

如果在循环中加载动态创建的视图,则滚动视图可以完美运行,例如。

 let customView = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 150))
 customView.frame = CGRect(x: i*300 , y: 0, width: 300, height: 150)
 customView.translatesAutoresizingMaskIntoConstraints = false
 scrollView.addSubview(customView)

但我有不同的目标。

在另一个XIB 我有一个图像视图和一个带有一些标签的堆栈视图。这个XIB 在情节提要中连接到一个扩展UIView 的类SingleEvent

我想做以下事情:

  1. 使用XIB 作为一种“蓝图”,并在我的滚动视图中多次加载同一个视图;
  2. 向任何实例传递一些数据;

这可能吗?

我尝试以这种方式加载 XIB 的内容:

 let customView = Bundle.main.loadNibNamed("SingleEvent", owner: self, options: nil)?.first as? SingleEvent

这样:

let customView = SingleEvent()

第一个使应用程序崩溃,而第二个没有问题,但我看不到任何效果(它不加载任何内容)。

我最新SingleEvent的内容如下:

 import UIKit

    class SingleEvent: UIView {

        @IBOutlet weak var label:UILabel!
        @IBOutlet weak var imageView:UIImageView!

        override init(frame: CGRect) {
            super.init(frame: frame)

            loadViewFromNib()
        }

        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)

            loadViewFromNib()
        }

        func loadViewFromNib() -> UIView {
            let myView = Bundle.main.loadNibNamed("SingleEvent", owner: self, options: nil)?.first as! UIView
            return myView
        }
    }

在此先感谢,任何帮助表示赞赏:-)

【问题讨论】:

  • 嗨!您的第二种方式调用 common init 但您的 loadViewFromNib() 函数在 init(frame: CGRect) 内部调用,所以尝试像这样初始化您的视图: let customView = SingleEvent(frame: someFrame)
  • @Ildar.Z:它不起作用
  • 添加了答案以使其更清晰

标签: ios swift uiview scrollview xib


【解决方案1】:

有多种方法可以从 xibs 加载自定义视图(类)。您可能会发现这种方法更容易一些。

首先,像这样创建您的 xib:

请注意,File's Owner 的类是默认的 (NSObject)。

相反,将您的自定义类分配给 xib 中的“根”视图:

现在,我们的整个自定义视图类如下所示:

class SingleEvent: UIView {

    @IBOutlet var topLabel: UILabel!
    @IBOutlet var middleLabel: UILabel!
    @IBOutlet var bottomLabel: UILabel!

    @IBOutlet var imageView: UIImageView!

}

而且,我们没有将loadNibNamed(...) 放在我们的自定义类中,而是创建了一个UIView 扩展:

extension UIView {
    class func fromNib<T: UIView>() -> T {
        return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
    }
}

要加载和使用我们的自定义类,我们可以这样做:

class FromXIBViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // create an instance of SingleEvent from its xib/nib
        let v = UIView.fromNib() as SingleEvent

        // we're going to use auto-layout & constraints
        v.translatesAutoresizingMaskIntoConstraints = false

        // set the text of the labels
        v.topLabel?.text    = "Top Label"
        v.middleLabel?.text = "Middle Label"
        v.bottomLabel?.text = "Bottom Label"

        // set the image
        v.imageView.image = UIImage(named: "myImage")

        // add the SingleEvent view
        view.addSubview(v)

        // constrain it 200 x 200, centered X & Y
        NSLayoutConstraint.activate([
            v.widthAnchor.constraint(equalToConstant: 200.0),
            v.heightAnchor.constraint(equalToConstant: 200.0),
            v.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            v.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            ])

    }

}

结果是:

还有...这是一个加载 10 个 SingleEvent 视图实例并将它们添加到垂直滚动视图的示例:

class FromXIBViewController: UIViewController {

    var theScrollView: UIScrollView = {
        let v = UIScrollView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .cyan
        return v
    }()

    var theStackView: UIStackView = {
        let v = UIStackView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.axis = .vertical
        v.alignment = .fill
        v.distribution = .fill
        v.spacing = 20.0
        return v
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        // add the scroll view to the view
        view.addSubview(theScrollView)

        // constrain it 40-pts on each side
        NSLayoutConstraint.activate([
            theScrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40.0),
            theScrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -40.0),
            theScrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 40.0),
            theScrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -40.0),
            ])

        // add a stack view to the scroll view
        theScrollView.addSubview(theStackView)

        // constrain it 20-pts on each side
        NSLayoutConstraint.activate([
            theStackView.topAnchor.constraint(equalTo: theScrollView.topAnchor, constant: 20.0),
            theStackView.bottomAnchor.constraint(equalTo: theScrollView.bottomAnchor, constant: -20.0),
            theStackView.leadingAnchor.constraint(equalTo: theScrollView.leadingAnchor, constant: 20.0),
            theStackView.trailingAnchor.constraint(equalTo: theScrollView.trailingAnchor, constant: -20.0),

            // stackView width = scrollView width -40 (20-pts padding on left & right
            theStackView.widthAnchor.constraint(equalTo: theScrollView.widthAnchor, constant: -40.0),
            ])


        for i in 0..<10 {

            // create an instance of SingleEvent from its xib/nib
            let v = UIView.fromNib() as SingleEvent

            // we're going to use auto-layout & constraints
            v.translatesAutoresizingMaskIntoConstraints = false

            // set the text of the labels
            v.topLabel?.text    = "Top Label: \(i)"
            v.middleLabel?.text = "Middle Label: \(i)"
            v.bottomLabel?.text = "Bottom Label: \(i)"

            // set the image (assuming we have images named myImage0 thru myImage9
            v.imageView.image = UIImage(named: "myImage\(i)")

            theStackView.addArrangedSubview(v)

        }

    }

}

结果:

【讨论】:

  • 首先,感谢您的热情回复。我的情况有点不同。在代码的最后一部分,我看到您从 UIViewController 加载 SingleEvents。我实际上不在视图控制器中:我在从嵌入滚动视图的 awakeFromNib 加载的 UIView 中。我无法详细描述这个项目,它相当复杂,我只是重构了它的一部分(我应该把“愚蠢”的工具提示变成“聪明”的工具提示)。现在我正在寻找一种在加载任何子视图之前将字典传递给此滚动视图的方法
  • 无论您是从UIViewController 还是从其他一些自定义UIView 实例化您的自定义视图都没有关系。没有区别。
  • 好吧,我只是想说一些你在回复之前可能刚刚知道的事情:也就是说,总有一个上下文:-)
【解决方案2】:

好的,我明白了。问题可能实际上是 loadViewFromNib 函数从 xib 返回 UIView,但您并没有以任何方式使用它。
让我们试试这个方法:
1) 使您的 loadViewFromNib 函数静态

    // Return our SingleEvent instance here  
    static func loadViewFromNib() -> SingleEvent {
            let myView = Bundle.main.loadNibNamed("SingleEvent", owner: self, options: nil)?.first as! SingleEvent
            return myView
        }

2) 删除 SingleEvent 类中的所有初始化

3)像这样在需要的地方初始化它:

let customView = SingleView.loadViewFromNib()

要在视图中传递数据,您可以在 SingleView 类中创建新函数:

func configureView(with dataModel:DataModel) {
   //Set data to IBOutlets here
}

并像这样从外部使用它:

let customView = SingleView.loadViewFromNib()
let dataModel = DataModel()
customView.configureView(with: dataModel)

【讨论】:

  • 好的,但是如何将数据传递到任何单个视图?
  • @Planemo 我更新了答案,请检查!希望对您有所帮助!
  • 我正要尝试你的解决方案,但我必须说我看到了一个我刚刚看到的问题:我只加载了一个子视图(我可以从右到左滚动,但在第一个之后子视图我没有看到其他实例)
  • 我认为 configureView 不能以这种方式使用,因为使用 loadViewFromNib 您是从静态函数返回视图,而不是从实例:那么,我如何引用插座?我想唯一的地方是 loadViewFromNib
  • @Planemo 对不起,我更新了答案。静态函数从 Bundle 返回一个 SingleEvent 的实例,这是一种常见的方法,应该可以正常工作。请确保 SingleView 的每个实例都必须在根视图中具有自己的框架位置(在您的情况下,在滚动视图中)。对你来说正确吗?
猜你喜欢
  • 2015-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-14
  • 1970-01-01
  • 2010-10-28
  • 2014-12-24
  • 2016-09-28
相关资源
最近更新 更多