何时在 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(因为需要实现该方法才能使编译成功)。
我想知道在决定 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(因为需要实现该方法才能使编译成功)。