iOS自定义视图 - 非常奇怪的属性行为

时间:2022-01-16 17:27:02

Using Swift 2.0

使用Swift 2.0

I'm trying to use a custom view with a label (not just that) to show some information on the screen, so my idea was to programatically add the custom view to a scrollview on the ViewController, and set the label's text (to start with)

我正在尝试使用带有标签的自定义视图(不仅仅是那个)在屏幕上显示一些信息,所以我的想法是以编程方式将自定义视图添加到ViewController上的滚动视图,并设置标签的文本(开始)用)

Problem: I just can't get a moment where both the label and the string i want to set the label's text to are not null, and for some reason the string gets null in the end.

问题:我只是无法得到标签和我想要设置标签文本的字符串不为空的时刻,并且由于某种原因,字符串最后变为空。

So i made a extensive debugging to show what's the state for each var in each moment, and that made me even more confused: (this is Xcode's log output)

所以我进行了大量的调试,以显示每个时刻每个var的状态,这让我更加困惑:(这是Xcode的日志输出)

my init was called, fatura String value: Optional("random text")
init with coder was called

 - On awake from Nib
Label outlet is NOT null
fatura String is null
view loaded from Nib was added

 - On Setup
Label outlet is null
fatura String is NOT null
checking fatura String value from ViewController: Optional("random text")

 - On ViewController's viewDidLoad
Label outlet is null
fatura String is NOT null

 - On ViewController's viewWillAppear
Label outlet is null
fatura String is NOT null

 - On Button touched action
Label outlet is NOT null
fatura String is null

I'm new to iOS, coming from some years in android development,and I would like to really understand what's going on, so any good articles and books would be welcome as well. I've found a lot of stuff on custom views, but they all are somewhat different , so I don't know which one to use, and they're mostly implementations only, no explanation whatsoever.

我是iOS的新手,来自Android开发的几年,我想真正了解正在发生的事情,所以任何好的文章和书籍都会受到欢迎。我在自定义视图上发现了很多东西,但它们都有些不同,所以我不知道使用哪一个,它们主要是实现,没有任何解释。

Questions:

  • Why does the vencimentoLb Label's outlet starts out as "not null", before the Nib view was loaded?

    为什么在加载Nib视图之前,vencimentoLb Label的出口是“非空”?

  • Why is the fatura String null, after* my init was called, which sets it out? *at least that's what the log tells me

    为什么在调用*我的init之后,fatura String为null? *至少这是日志告诉我的

  • Why the hell is my fatura String null on the buttonClicked action??

    为什么地狱是我的fatura字符串null buttonClicked动作?

To Summarize: how can i get both the label and the string not null, so to set the text on the label?

总结:如何让标签和字符串都不为空,那么要在标签上设置文本?

I know it's a lot of questions, so an answer for any of them is much appreciated

我知道这是很多问题,因此非常感谢他们中的任何一个

"Solution" found:

call customview.view to force the outlets to get instanciated. that did not work (the customview has no view member apart from the one I created, and calling that one made no difference)

调用customview.view强制出口实现。不起作用(customview没有我创建的视图成员,并调用那个没有区别)

Custom View's code:

自定义视图的代码:

class FaturaCard: UIView {

    var view:UIView!
    @IBOutlet var vencimentoLb: UILabel!
    @IBOutlet var cardView: UIView!

    var fatura:String?

    init(string:String , frame:CGRect){
        fatura = string
        print("my init was called, fatura String value:",self.fatura)
        super.init(frame: frame)
        setup()
    }

    override init(frame: CGRect){
        super.init(frame:frame)
        print("init with frame was called")
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        print("init with coder was called")
    }

    @IBAction func onBotaoClicked(sender: AnyObject) {
        print("\n - On Button touched action")
        checksWhatsNull()
    }

    func setup(){
        view = loadViewFromNib()
        view.frame = bounds
        view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        addSubview(view)
        print("view loaded from Nib was added")
        print("\n - On Setup")
        checksWhatsNull()
    }

    func checksWhatsNull(){
        if vencimentoLb == nil{
            print("Label outlet is null")
        }else{
            print("Label outlet is NOT null")
        }

        if self.fatura == nil{
            print("fatura String is null")
        }else{
            print("fatura String is NOT null")
        }
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        print("\n - On awake from Nib")
        checksWhatsNull()
    }

    func loadViewFromNib() -> UIView{
        let nib = UINib(nibName: "FaturaCard", bundle: nil)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
        return view
    }

}

Edit:

Custom view instantiation code:

自定义视图实例化代码:

var view2:FaturaCard!
override func viewDidLoad() {
        super.viewDidLoad()

        view2 = FaturaCard(string:"random text",frame: CGRect(x: 00, y:145, width: scrollView.bounds.size.width, height: 145))

        print("checking fatura String value from ViewController:",view2.fatura)

        self.scrollView.addSubview(view2)

        print("\n - On ViewController's viewDidLoad")
        view2.checksWhatsNull()

        self.scrollView.contentSize.height=145*2
    }

2 个解决方案

#1


1  

You have two outputs from init statements in the log in your question.

您的问题中的日志中有两个来自init语句的输出。

This means that you are loading two instances of the custom view.

这意味着您正在加载自定义视图的两个实例。

The logs you are seeing are from different instances, depending on the action - values aren't changing, you are seeing the values from different instances.

您看到的日志来自不同的实例,具体取决于操作 - 值未更改,您将看到来自不同实例的值。

Adding print(self) to your logging should hopefully make this clear.

在您的日志记录中添加print(self)应该可以清楚地说明这一点。

init(coder:) is going to be when you create a view from a nib or storyboard. You are creating the view with a custom init (which you see in the logs), then creating a second instance of the view in the setUp method, and holding it within the original. This isn't right.

init(编码器:)将在您从笔尖或故事板创建视图时使用。您正在使用自定义init(在日志中看到)创建视图,然后在setUp方法中创建视图的第二个实例,并将其保存在原始视图中。这不对。

I think you've read about accessing view to force a view to be loaded, and tried to do that here - this applies to view controllers, not views, and does not apply at all to your case.

我想你已经阅读过有关访问视图以强制加载视图的信息,并尝试在此处执行此操作 - 这适用于视图控制器,而不适用于视图,并且根本不适用于您的情况。

You should probably be creating the view directly from the nib, then setting the fatura property - it isn't possible to tell from what's in the question, but I assume your outlets are pointing to the main view itself rather than to file's owner, so you can create with an owner of nil.

您可能应该直接从笔尖创建视图,然后设置fatura属性 - 无法判断问题中的内容,但我认为您的出口指向主视图本身而不是文件的所有者,所以您可以使用零所有者创建。

Looking at your updated code, you probably want something like this.

查看更新的代码,您可能想要这样的东西。

Instead of this line in viewDidLoad:

而不是viewDidLoad中的这一行:

view2 = FaturaCard(string:"random text",frame: CGRect(x: 00, y:145, width: scrollView.bounds.size.width, height: 145))

Use this:

let nib = UINib(nibName: "FaturaCard", bundle: nil)
view2 = nib.instantiateWithOwner(nil, options: nil)[0] as! FaturaCard
view2.frame = CGRect(x: 00, y:145, width: scrollView.bounds.size.width, height: 145)
view2.fatura = "Random Text"

It's awkward loading views from nibs, ideally you would put this stuff in a class method on FaturaCard where you can also pass in a frame and a string.

这是从笔尖加载视图的尴尬,理想情况下你会把这些东西放在FaturaCard的类方法中,你也可以传入一个框架和一个字符串。

#2


0  

So, in the end the problem was in the Interface Builder.

所以,最终问题出在Interface Builder中。

I've set the Custom Class property on Identity Inspector in the main view of the nib, instead of in the File's Owner placeholder. That created an instance of the 'FaturaCard' custom view inside the FaturaCard's view, creating thus two instances at once(as said on jrturton's answer) the second one initialized by init(coder:), which created all that trouble.

我在nib的主视图中的Identity Inspector上设置了Custom Class属性,而不是在File的Owner占位符中。这在FaturaCard的视图中创建了一个'FaturaCard'自定义视图的实例,同时创建了两个实例(如jrturton的回答所述),第二个由init(编码器:)初始化,这创造了所有麻烦。

It was a beginner's mistake, a very confusing one.

这是一个初学者的错误,一个非常令人困惑的错误。

#1


1  

You have two outputs from init statements in the log in your question.

您的问题中的日志中有两个来自init语句的输出。

This means that you are loading two instances of the custom view.

这意味着您正在加载自定义视图的两个实例。

The logs you are seeing are from different instances, depending on the action - values aren't changing, you are seeing the values from different instances.

您看到的日志来自不同的实例,具体取决于操作 - 值未更改,您将看到来自不同实例的值。

Adding print(self) to your logging should hopefully make this clear.

在您的日志记录中添加print(self)应该可以清楚地说明这一点。

init(coder:) is going to be when you create a view from a nib or storyboard. You are creating the view with a custom init (which you see in the logs), then creating a second instance of the view in the setUp method, and holding it within the original. This isn't right.

init(编码器:)将在您从笔尖或故事板创建视图时使用。您正在使用自定义init(在日志中看到)创建视图,然后在setUp方法中创建视图的第二个实例,并将其保存在原始视图中。这不对。

I think you've read about accessing view to force a view to be loaded, and tried to do that here - this applies to view controllers, not views, and does not apply at all to your case.

我想你已经阅读过有关访问视图以强制加载视图的信息,并尝试在此处执行此操作 - 这适用于视图控制器,而不适用于视图,并且根本不适用于您的情况。

You should probably be creating the view directly from the nib, then setting the fatura property - it isn't possible to tell from what's in the question, but I assume your outlets are pointing to the main view itself rather than to file's owner, so you can create with an owner of nil.

您可能应该直接从笔尖创建视图,然后设置fatura属性 - 无法判断问题中的内容,但我认为您的出口指向主视图本身而不是文件的所有者,所以您可以使用零所有者创建。

Looking at your updated code, you probably want something like this.

查看更新的代码,您可能想要这样的东西。

Instead of this line in viewDidLoad:

而不是viewDidLoad中的这一行:

view2 = FaturaCard(string:"random text",frame: CGRect(x: 00, y:145, width: scrollView.bounds.size.width, height: 145))

Use this:

let nib = UINib(nibName: "FaturaCard", bundle: nil)
view2 = nib.instantiateWithOwner(nil, options: nil)[0] as! FaturaCard
view2.frame = CGRect(x: 00, y:145, width: scrollView.bounds.size.width, height: 145)
view2.fatura = "Random Text"

It's awkward loading views from nibs, ideally you would put this stuff in a class method on FaturaCard where you can also pass in a frame and a string.

这是从笔尖加载视图的尴尬,理想情况下你会把这些东西放在FaturaCard的类方法中,你也可以传入一个框架和一个字符串。

#2


0  

So, in the end the problem was in the Interface Builder.

所以,最终问题出在Interface Builder中。

I've set the Custom Class property on Identity Inspector in the main view of the nib, instead of in the File's Owner placeholder. That created an instance of the 'FaturaCard' custom view inside the FaturaCard's view, creating thus two instances at once(as said on jrturton's answer) the second one initialized by init(coder:), which created all that trouble.

我在nib的主视图中的Identity Inspector上设置了Custom Class属性,而不是在File的Owner占位符中。这在FaturaCard的视图中创建了一个'FaturaCard'自定义视图的实例,同时创建了两个实例(如jrturton的回答所述),第二个由init(编码器:)初始化,这创造了所有麻烦。

It was a beginner's mistake, a very confusing one.

这是一个初学者的错误,一个非常令人困惑的错误。