何时在 ViewController 上使用 View

When to use a View over a ViewControlller

我想知道在决定 class 必须是 UIview 或 UIviewcontroller 时最佳做法是什么。

我正在开发一个应用程序,该应用程序在单个 Viewcontroller 中同时使用多个 class 和视图。我有一个动态创建按钮的视图。此视图与 ViewController 中的许多其他视图并存。由于此视图实现了相当多的代码行,因此我将其放在一个单独的 class 中。那么问题是:它应该扩展 UiView 还是 UIViewcontroller?

此视图将具有一些属性,例如填充、可用尺寸等。这些取决于其父级 Viewcontroller 的自动布局锚点,它是其子级class。如果我使用 UIview,这些将不会在 Init 上实例化。

为什么,我应该选择一个而不是另一个?

class 标签控制器:UIView {

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

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}


var buttons : [UIButton: String]?

var sidePadding = CGFloat(15);
var topPadding = CGFloat(15);
var spacing = CGFloat(10);

var availableHeight : CGFloat!
var availableWidth :  CGFloat!

func createButton (title: String) -> UIButton {
    let maxWidth : CGFloat = 180;
    let button = UIButton();

    button.setTitle(title, for: .normal)
    button.titleLabel?.font = UIFont(name: "Avenir-Light", size: 15.0)
    button.translatesAutoresizingMaskIntoConstraints = false;
    self.addSubview(button)
    button.frame = CGRect(x:0,y:0,width:button.intrinsicContentSize.width, height: button.intrinsicContentSize.height)
    if button.frame.width > maxWidth { button.widthAnchor.constraint(equalToConstant: maxWidth).isActive = true }
    button.backgroundColor = .blue
   // button.addTarget(self, action: #selector(self.onButtonPresed(_:)), for: .touchUpInside);
    return button;
}


public func addButton(name: String) {
    let button = createButton(title: name)
    setConstriants(button: button)
}

private func setConstriants(button: UIButton) {

        for label in buttons {

        if totalHeight + 50 > availableHeight { createMoreButton(topPadding); break };

        let button = createButton(buttonText: label)
        let buttonWidth = button.intrinsicContentSize.width;
        let buttonHeight = button.intrinsicContentSize.height;

        if buttonWidth > availableWidth {
            button.widthAnchor.constraint(equalToConstant:  availableWidth  - sidePadding).isActive = true;
        }
        if rowLength == 0 && rowCount == 0
        {
            setFirstButtonConstraint(button: button, totalHeight: totalHeight, sidePadding: sidePadding)
            rowLength += buttonWidth + sidePadding // FIX annoying first row image overlap

        }
        else if rowLength + buttonWidth + padding < availableWidth
        {
            setConstraint(button: button, lastButton: lastButton, totalHeight: totalHeight, padding: padding)
            rowLength += buttonWidth + padding;
        }
        else
        {

            totalHeight += buttonHeight + padding
            rowLength = buttonWidth + sidePadding;
            rowCount += 1;
            setNewRowConstraint(button: button, totalHeight:totalHeight , sidePadding: sidePadding)
        }
        indexOfLastButton += 1;
        lastButton = button
        displayedButtons.append(button)
        print("Buttons displayed   \(displayedButtons.count)")
    }

    pulsate_buttons(duration: 0.1)
}

private func removeDisplayedButtons() {
    for _ in 0...displayedButtons.count {
        displayedButtons.popLast()?.removeFromSuperview();
    }
};

private func setFirstButtonConstraint(button: UIButton, totalHeight: CGFloat, sidePadding: CGFloat) {
    button.topAnchor.constraint(equalTo: self.topAnchor, constant: totalHeight).isActive = true;
    button.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: sidePadding + 5).isActive = true;
}
private func setConstraint(button: UIButton, lastButton: UIButton, totalHeight: CGFloat, padding:CGFloat) {
    button.leadingAnchor.constraint(equalTo: lastButton.trailingAnchor, constant: padding).isActive = true;
    button.topAnchor.constraint(equalTo: self.topAnchor, constant: totalHeight).isActive = true;
}
private func setNewRowConstraint(button: UIButton, totalHeight: CGFloat, sidePadding: CGFloat) {
    button.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: sidePadding).isActive = true;
    button.topAnchor.constraint(equalTo: self.topAnchor, constant: totalHeight).isActive = true;
}

private func createMoreButton(_ topPadding: CGFloat) {
    if moreButton != nil { return }
    let btn = UIButton()
    btn.setImage(UIImage(named: "suggestionCloud_more_button.png"), for: .normal)
    let buttonText = "Moooar.."
    btn.setTitle(buttonText, for: .normal)
    btn.setTitleColor(.black, for: .normal)
    btn.titleLabel?.font = UIFont(name: "Avenir-Light", size: 15.0)
    self.addSubview(btn)
    moreButton = btn
    btn.frame = CGRect(x: (self.frame.size.width/2 - 20), y: self.frame.size.height * 0.85, width: 40, height: 15)
    btn.addTarget(self, action: #selector(showMoreButtons(_:)), for: .touchUpInside)
}

@objc func showMoreButtons(_: UIButton) {
    pulsate_view(duration: 0.1)
    if indexOfLastButton == labelStore.count { indexOfLastButton = 0; return createLayout(buttons : labelStore)}
    let buttonsToBeDisplayed : [String] = Array(labelStore[indexOfLastButton...])
    createLayout(buttons: buttonsToBeDisplayed)
}

我喜欢这样想:UIView 托管和管理离散内容,而 UIViewController 托管和管理由许多离散内容组成的应用程序内容。

您可以从桌面应用程序的角度来考虑它,其中 "window" 是 UIViewController,而此 window 的元素(按钮、工具栏等)是 UIViews(您还可以在 UIViewController 内托管其他 UIViewControllers)。

一般来说,UIViews 不应决定应用程序导航逻辑,而应将其推迟到 UIViewController

简单来说,如果某物以模态方式呈现或占据整个屏幕,则它可能意味着 UIViewController

传统上,Apple 采用 MVC 范例 (Model/View/Controller)。如果您的内容是模型(一个人的出生日期,您拍摄的照片),则视图是显示它的地方,而控制器则在内容管理(持久性、检索)和内容显示之间进行调解。

顺便说一句:查看 UIStackView 按钮行。


奖励:如何为以编程方式实例化的 UIView.

创建自定义初始化器

如果您的视图只是以编程方式创建(而不是通过界面构建​​器实例化),您可以为它定义一个必需的初始化程序:

class MyView: UIView {
    var required: Bool
    var anotherRequiredVar: String

    required init(frame: CGRect, something: Bool, somethingElse: String) {
        required = something
        anotherRequiredVar = somethingElse
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init with coder not implemented")
    }
}

在此示例中,init?(coder) 方法抛出异常。当取消归档您的 Storyboard 或 Nib 内容时,此初始值设定项由界面构建器 运行 提供。由于您不需要它,您可以抛出一个 fatalError(因为需要实现该方法才能使编译成功)。