变量更改时更新 UILabel 文本
Updating UILabel text when variable changes
我有一个 UIControl class,需要根据 UIImageView 的位置做一些计算,它可以用 touchesBegan 和 touchesMoved 移动(这个 class 里面的所有东西)。
我想将它显示为我以编程方式创建的 UILabel。
class control : UIControl{
...
let leftControl: UIImageView = UIImageView(image: UIImage(named: "left-control"))
...
func leftValue() -> String{
var leftValue : String = "0.0"
leftValue = "\(leftControl.center.x)"
return leftValue
}
}
和我的ViewController.swift
class ViewController: UIViewController {
let ctrl : Control = Control()
let leftLabel : UILabel = UILabel(frame: CGRect(x: 40, y: 300, width: 150, height: 30))
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
ctrl.frame.origin = CGPoint(x: 40, y: 400)
leftLabel.text = "\(ctrl.leftValue())" //displays only starting value
view.addSubview(slider)
view.addSubview(leftLabel)
view.addSubview(rightLabel)
}
我知道它在 viewDidLoad 中,所以它没有正确更新。我想知道 scheduledTimer,但不知道它是否是好的解决方案。
您可以使用协议和委派实现此目的 - 在您的 Control
文件中添加:
protocol ControlDelegate: class {
func controlPositionDidChange(leftValue: String)
}
并在Control
里面加一个weak var delegate: ControlDelegate?
class.
在视图控制器的文件中进行以下更改:
class ViewController: UIViewController, ControllDelegate {
let ctrl : Control = Control()
let leftLabel : UILabel = UILabel(frame: CGRect(x: 40, y: 300, width: 150, height: 30))
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
ctrl.frame.origin = CGPoint(x: 40, y: 400)
ctrl.delegate = self
leftLabel.text = "\(ctrl.leftValue())" //displays only starting value
view.addSubview(slider)
view.addSubview(leftLabel)
view.addSubview(rightLabel)
}
func controlPositionDidChange(leftValue: String) {
leftLabel.text = leftValue
}
}
现在,只要您想通知代理您的控件已更改位置,只需在适当的位置调用 self.delegate?.controlPositionDidChange(self.leftValue())
。
和往常一样,more in the docs. 我强烈建议通读它们,因为委派和协议在 CocoaTouch 中被广泛使用。
@Losiowaty 的回答描述了大多数开发者选择的解决方案。但是(在我看来)有更好的方法来实现它。我更喜欢面向对象的解决方案。它可能看起来像更多的代码,但它是一种具有更多可重用代码的更好的可维护方式。
您的问题的真正面向对象的解决方案可能如下所示:
// reusable protocol set
protocol OOString: class {
var value: String { get set }
}
// reusable functional objects
final class RuntimeString: OOString {
init(initialValue: String = "") {
self.value = initialValue
}
var value: String
}
final class ViewUpdatingString: OOString {
init(_ decorated: OOString, view: UIView) {
self.decorated = decorated
self.view = view
}
var value: String {
get {
return decorated.value
}
set(newValue) {
decorated.value = newValue
view.setNeedsLayout()
}
}
private let decorated: OOString
private let view: UIView
}
// reusable ui objects
final class MyLabel : UILabel {
init(frame: CGRect, imageXCenter: OOString) {
self.imageXCenter = imageXCenter
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("Not supported")
}
override func layoutSubviews() {
super.layoutSubviews()
text = imageXCenter.value
}
private let imageXCenter: OOString
}
final class MyControl : UIControl {
init(frame: CGRect, imageXCenter: OOString) {
self.imageXCenter = imageXCenter
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("Not supported")
}
private let imageXCenter: OOString
private let leftControl = UIImageView(image: UIImage(named: "left-control"))
// call this at change
private func updateValue() {
imageXCenter.value = "\(leftControl.center.x)"
}
}
// non reusable business logic
final class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let dependency = RuntimeString(initialValue: "unset")
let leftLabel = MyLabel(
frame: CGRect(x: 40, y: 300, width: 150, height: 30),
imageXCenter: dependency
)
let control = MyControl(
frame: CGRect(x: 40, y: 400, width: 400, height: 400),
imageXCenter: ViewUpdatingString(dependency, view: leftLabel)
)
view.addSubview(leftLabel)
view.addSubview(control)
}
}
主要思想是将两个对象的依赖关系提取到另一个对象中,然后使用装饰器自动更新每个设置值的ui。
注:
- 这种方法遵循面向对象编码的规则(干净的编码、优雅的对象、装饰器模式……)
- 可重复使用 类 构造非常简单,仅能完成一项任务
- 类 通过协议相互通信
- 依赖尽可能通过依赖注入给出
- 内部对象功能是私有的(松耦合)
- 一切(业务逻辑除外)都是为重用而设计的 -> 如果您这样编码,可重用代码的组合会随着您编码的每一天而增长
- 你的应用程序的业务逻辑更集中在一个地方(在我真正的编码中它甚至在 UIViewController 之外)
- 当对协议使用虚假实现时,可重用对象的单元测试非常简单(在大多数情况下甚至不需要模拟)
- 保留循环的问题较少,在大多数情况下您不再需要弱属性
- 避免 Null、nil 和 Optionals(它们会污染你的代码)
- ...
我有一个 UIControl class,需要根据 UIImageView 的位置做一些计算,它可以用 touchesBegan 和 touchesMoved 移动(这个 class 里面的所有东西)。 我想将它显示为我以编程方式创建的 UILabel。
class control : UIControl{
...
let leftControl: UIImageView = UIImageView(image: UIImage(named: "left-control"))
...
func leftValue() -> String{
var leftValue : String = "0.0"
leftValue = "\(leftControl.center.x)"
return leftValue
}
}
和我的ViewController.swift
class ViewController: UIViewController {
let ctrl : Control = Control()
let leftLabel : UILabel = UILabel(frame: CGRect(x: 40, y: 300, width: 150, height: 30))
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
ctrl.frame.origin = CGPoint(x: 40, y: 400)
leftLabel.text = "\(ctrl.leftValue())" //displays only starting value
view.addSubview(slider)
view.addSubview(leftLabel)
view.addSubview(rightLabel)
}
我知道它在 viewDidLoad 中,所以它没有正确更新。我想知道 scheduledTimer,但不知道它是否是好的解决方案。
您可以使用协议和委派实现此目的 - 在您的 Control
文件中添加:
protocol ControlDelegate: class {
func controlPositionDidChange(leftValue: String)
}
并在Control
里面加一个weak var delegate: ControlDelegate?
class.
在视图控制器的文件中进行以下更改:
class ViewController: UIViewController, ControllDelegate {
let ctrl : Control = Control()
let leftLabel : UILabel = UILabel(frame: CGRect(x: 40, y: 300, width: 150, height: 30))
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
ctrl.frame.origin = CGPoint(x: 40, y: 400)
ctrl.delegate = self
leftLabel.text = "\(ctrl.leftValue())" //displays only starting value
view.addSubview(slider)
view.addSubview(leftLabel)
view.addSubview(rightLabel)
}
func controlPositionDidChange(leftValue: String) {
leftLabel.text = leftValue
}
}
现在,只要您想通知代理您的控件已更改位置,只需在适当的位置调用 self.delegate?.controlPositionDidChange(self.leftValue())
。
和往常一样,more in the docs. 我强烈建议通读它们,因为委派和协议在 CocoaTouch 中被广泛使用。
@Losiowaty 的回答描述了大多数开发者选择的解决方案。但是(在我看来)有更好的方法来实现它。我更喜欢面向对象的解决方案。它可能看起来像更多的代码,但它是一种具有更多可重用代码的更好的可维护方式。
您的问题的真正面向对象的解决方案可能如下所示:
// reusable protocol set
protocol OOString: class {
var value: String { get set }
}
// reusable functional objects
final class RuntimeString: OOString {
init(initialValue: String = "") {
self.value = initialValue
}
var value: String
}
final class ViewUpdatingString: OOString {
init(_ decorated: OOString, view: UIView) {
self.decorated = decorated
self.view = view
}
var value: String {
get {
return decorated.value
}
set(newValue) {
decorated.value = newValue
view.setNeedsLayout()
}
}
private let decorated: OOString
private let view: UIView
}
// reusable ui objects
final class MyLabel : UILabel {
init(frame: CGRect, imageXCenter: OOString) {
self.imageXCenter = imageXCenter
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("Not supported")
}
override func layoutSubviews() {
super.layoutSubviews()
text = imageXCenter.value
}
private let imageXCenter: OOString
}
final class MyControl : UIControl {
init(frame: CGRect, imageXCenter: OOString) {
self.imageXCenter = imageXCenter
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("Not supported")
}
private let imageXCenter: OOString
private let leftControl = UIImageView(image: UIImage(named: "left-control"))
// call this at change
private func updateValue() {
imageXCenter.value = "\(leftControl.center.x)"
}
}
// non reusable business logic
final class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let dependency = RuntimeString(initialValue: "unset")
let leftLabel = MyLabel(
frame: CGRect(x: 40, y: 300, width: 150, height: 30),
imageXCenter: dependency
)
let control = MyControl(
frame: CGRect(x: 40, y: 400, width: 400, height: 400),
imageXCenter: ViewUpdatingString(dependency, view: leftLabel)
)
view.addSubview(leftLabel)
view.addSubview(control)
}
}
主要思想是将两个对象的依赖关系提取到另一个对象中,然后使用装饰器自动更新每个设置值的ui。
注:
- 这种方法遵循面向对象编码的规则(干净的编码、优雅的对象、装饰器模式……)
- 可重复使用 类 构造非常简单,仅能完成一项任务
- 类 通过协议相互通信
- 依赖尽可能通过依赖注入给出
- 内部对象功能是私有的(松耦合)
- 一切(业务逻辑除外)都是为重用而设计的 -> 如果您这样编码,可重用代码的组合会随着您编码的每一天而增长
- 你的应用程序的业务逻辑更集中在一个地方(在我真正的编码中它甚至在 UIViewController 之外)
- 当对协议使用虚假实现时,可重用对象的单元测试非常简单(在大多数情况下甚至不需要模拟)
- 保留循环的问题较少,在大多数情况下您不再需要弱属性
- 避免 Null、nil 和 Optionals(它们会污染你的代码)
- ...