在另一个xib中使用xib对象

19

我正在使用 IB 设计一个特定的按钮(带有一些标签、imageView 和其他内容)。

然后,我想在另一个 xib 中使用该 xib 对象(即按钮),其中我有 6 个该按钮的对象。

我知道可以使用类标识符在编程时实现这一点,但那样我就必须在代码中定位我的标签和 imageView,并设置默认值和所有其他内容。

我想知道是否可能仅使用 IB 来做同样的事情?(当然,我仍将在代码中设置值,但我希望在 xib 中设置标签/imageView 的位置和所有其他内容)

谢谢

编辑

所以,我明白了我一开始要求的不可能实现。 现在我正在尝试这样做:

我创建了一个 ButtonTest.xib。 在 Xib 中,我有一个 UIView 和 3 个子视图(2 个标签和一个按钮)。 在检查器中,我将 UIView 类设置为 ButtonTest。 在 ButtonTest.m 中,我有每个子视图的 3 个 outlets,它们在 IB 中连接。

接下来我有一个 ButtonTestViewController.xib。 在里面,我放置了一个视图,并将其类在检查器中设置为 ButtonTest。 我将该视图连接到 ButtonTestViewController.m 中的一个名为 myTextView 的 outlet,该类为 ButtonTest

现在,这是 ButtonTestViewController.m viewDidLoad 中的代码:

- (void)viewDidLoad {
    [super viewDidLoad];

    NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"ButtonTest" owner:nil options:nil];
    ButtonTest *mainView = (ButtonTest*)[subviewArray objectAtIndex:0];

    self.myTextView = mainView;
}

我的期望是ButtonTestViewController.xib中的视图与我在ButtonTest.xib中设计的视图相同。但事实并非如此,ButtonTestViewController.xib内部的视图仍然保持不变。

值得一提的是,如果我添加:

[self.view addSubview:mainView];

它确实添加了新的视图,除了现有的视图。

有什么想法可以做到我想要的吗?

最终,我希望在 ButtonTestViewController.xib 中有 6 个视图,所有视图都看起来像 ButtonTest.xib 模板,并且标签值将在代码中设置。

Edit2

好的,伙计们,我按照你们说的做了,它完美地工作了。我目前唯一遇到的问题是当 ButtonTestViewController.xib 中的视图稍微大于 ButtonTest.xib 的视图时,按钮上的标签看起来非常模糊。当它们大小相同时,一切都很好。

@Ned - 我在我的 InitWithCoder 方法中使用了你发布的完全相同的代码,除了把 frame 语句换成了这个:

self.bounds = mainView.frame;

我尝试使用IB中的内容模式进行修复,但没有成功。

当我像您发布的那样使用它时,意思是:

mainView.frame = self.bounds;

我的代码完全不起作用了,两个视图大小都保持不变。 我尝试了resizeMask但仍无效。

有没有什么想法可以解决这两个问题? 谢谢大家!

编辑3

我成功解决了其中一个问题,将ButtonTestViewController.xib的大小调整为ButtonTest.xib的视图大小。 这是我所做的(使用代码解决模糊问题,引自这里

self.bounds = CGRectMake(0, 0, mainView.frame.size.width, mainView.frame.size.height);
        CGRect overlay2Frame = self.frame;
        overlay2Frame.origin.x = round(overlay2Frame.origin.x);
        overlay2Frame.origin.y = round(overlay2Frame.origin.y);
        self.frame = overlay2Frame;

另一个问题我仍然无法解决。ButtonTest.xib中的视图就是无法变得更大以匹配其他视图。

4个回答

16
你可以轻松地做到这一点。我通常会创建一个UIView子类(比如"MyView.m"和"MyView.h"),然后再创建一个名为"MyView.xib"的nib文件。
首先,在nib文件中,点击File's Owner,并将其类设置为"MyView"。这样IB将显示来自你的类的IBOutlets/Actions。将单个顶级视图添加为主视图(如果它还没有),并将你的其他自定义元素(如UILabels和UIImageViews)作为主UIView的子视图添加进去。
接下来,添加以下代码,以便在初始化MyView时调用它(记住,如果你从nib初始化,则将通过- (id)initWithCoder:(NSCoder *)aDecoder进行初始化)。
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
UIView *mainView = [subviewArray objectAtIndex:0];

//Just in case the size is different (you may or may not want this)
mainView.frame = self.bounds;

[self addSubview:mainView];
这段代码的作用是从你的nib文件中加载视图层次结构到NSArray中,并且由于你只有一个顶级UIView,所以可以将其添加为自定义UIView的子视图。这样可以在Interface Builder中设计UIView(和其他UIView的子类)。
编辑:根据原帖的编辑进行了更新。
我一直都是按照上述步骤进行操作的。区别在于,在ButtonTest nib文件中,您更改了UIView的类为ButtonTest,但我将文件所有者的类更改为ButtonTest。然后,在ButtonTest.m中执行loadNibNamed的操作。现在,要将该对象添加到ButtonTestViewController.xib中,请添加一个UIView(或UIButton,无论ButtonTest是从哪个子类继承的),并将类更改为ButtonTest。
这有点令人困惑,所以我会尝试按文件来分解它。
ButtonTestViewController.xib:添加一个UIButton(ButtonTest继承自该按钮)并将其类更改为ButtonTest。
ButtonTest.m:在“initWithCoder”方法内,执行我上面提到的所有“loadNibNamed”操作。
ButtonTest.xib:将文件所有者类更改为ButtonTest,并链接所有IBOutlets和IBActions。

我已经编辑了我的答案,使其更加清晰。我认为这就是你想要做的事情。 - Ned
添加了一个关于此问题相关的新问题:https://dev59.com/DG445IYBdhLWcg3wAFcC - Idan
尝试使用调整大小掩码和“mainView.frame = self.bounds;”一起使用。你正在使用该行更改ButtonTest.xib中视图的框架,以匹配ButtonTestViewController.nib中ButtonTest对象的边界。如果所有元素都具有左上角的调整大小掩码,则不会看到任何更改,但如果您更改它们,则应该会看到更改。 - Ned
3
如果您将提供的代码放在 "initWithCoder" 中,那么在视图从不同的 XIB 创建时会创建一个无限循环,您确定吗? - Johnus
initWithCoder 调用 loadNibNamed 会创建无限循环,正如 @Johnus 上面所述。有什么办法可以摆脱它吗? - Tsiftis Karampouzouklis
显示剩余3条评论

8

有些人在评论中问,这个解决方案是否会创建无限循环,而我没有足够的声望来回答那里,所以这是我的答案:

如果您的XIB中的根视图设置为"MyView"的"自定义类",则会出现循环。这会导致从XIB加载的视图再次调用MyView的初始化程序,从而创建无限循环。解决方法是将"自定义类"设置为UIView,只有文件所有者的类应该设置为MyView(以便能够分配IBOutlets和IBActions)。


3

Swift 3.0

创建含有类的 Swift 文件

class ViewXIBfileClassView: UIView {
.....
}

使用ViewXIBfileClassView创建Xib文件

创建包装视图类

class ViewWrapper: UIView {

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        let view = UINib(nibName: "ViewXIBfileClassView_file", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! viewXIBfileClassView
        self.addSubview(view)
        view.bindEdgesToSuperview()
    }
}

UIView 添加扩展

extension UIView {
    /// Adds constraints to the superview so that this view has same size and position.
    /// Note: This fails the build if the `superview` is `nil` – add it as a subview before calling this.
    func bindEdgesToSuperview() {
        guard let superview = superview else {
            preconditionFailure("`superview` was nil – call `addSubview(view: UIView)` before calling `bindEdgesToSuperview()` to fix this.")
        }
        translatesAutoresizingMaskIntoConstraints = false
        ["H:|-0-[subview]-0-|", "V:|-0-[subview]-0-|"].forEach { visualFormat in
            superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: visualFormat, options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
        }
    }
}

在您想要的所有Xib文件中,使用带有ViewWrapperUIView


-1
如果你的类是UIButton的子类,你可以在Interface Builder中打开库,当你搜索它时,它将出现在类选项卡下,然后你可以拖放它。
你也可以只是拖放一个UIButton并在检查器面板中设置它的类。
但是,因为你有自己的.xib文件,这可能会弄乱它,如果是这种情况,请忽略我的建议。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接