我有UIViewController的这个沉重的basicVC Class子类,我想将其转换为vcprotocol。
这是基本的VC,可以完成所有工作,例如上课。我想作为vcProtocol进入。
我想做的是分开关注。并非所有ViewController都需要显示“警报视图”或“网络未连接味精”。
例如,我有在协议扩展中创建的indicatorView作为计算属性。没有错误警告,但没有显示任何指示器。当我尝试调试并执行po acticvityIndicator
时,出现以下错误,这表明从未分配activityIndicator。
error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x5a1012de027).
The process has been returned to the state before expression evaluation.
代码段:
protocol vcProtocol {
var activityIndicator: UIActivityIndicatorView { get }
}
协议扩展名:
extension vcProtocol where Self: UIViewController {
var activityIndicator: UIActivityIndicatorView {
let indicator = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.gray)
indicator.hidesWhenStopped = true
indicator.style = .whiteLarge
indicator.color = .red
indicator.backgroundColor = UIColor.gray
indicator.translatesAutoresizingMaskIntoConstraints = false
return indicator
}
func showLoadingIndicator() {
activityIndicator.startAnimating()
activityIndicator.isHidden = false
}
func hideLoadingIndicator() {
activityIndicator.stopAnimating()
activityIndicator.isHidden = true
}
}
我无法解决该问题。因为我只能在协议中具有计算的属性。 所以我把它们作为只获得属性。 我的计划是使用协议扩展来提供默认的实现。
关于如何解决此问题的任何想法。
答案 0 :(得分:2)
此activityIndicator
是一个计算的属性,因此,每次引用该计算的属性时,都会调用该get
块。最终结果是,按照书面规定,每次引用activityIndicator
时,您将获得一个UIActivityIndicatorView
的新实例,这显然不是您的意图。
考虑您的showLoadingIndicator
:
func showLoadingIndicator() {
activityIndicator.startAnimating()
activityIndicator.isHidden = false
}
第一行(带有startAnimating
)将返回一个新的UIActivityIndicatorView
,第二行(带有isHidden
)将返回另一个。而且这两个都不是您可能添加到子视图中的那个。
此activityIndicator
实际上应该实例化一次,并且只能实例化一次。不幸的是,您无法在扩展中定义存储的属性,因此有几种方法:
您可以让UIViewController
声明存储的属性,只需定义配置,显示和隐藏它的方法方法即可:
protocol LoadingIndicatorProtocol: class {
var loadingActivityIndicator: UIActivityIndicatorView? { get set }
}
extension LoadingIndicatorProtocol where Self: UIViewController {
func addLoadingIndicator() {
let indicator = UIActivityIndicatorView(style: .gray)
indicator.hidesWhenStopped = true
indicator.style = .whiteLarge
indicator.color = .red
indicator.backgroundColor = .gray
indicator.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(indicator)
// you might want to add the constraints here, too
loadingActivityIndicator = indicator
}
func showLoadingIndicator() {
loadingActivityIndicator?.startAnimating()
loadingActivityIndicator?.isHidden = false
}
func hideLoadingIndicator() {
loadingActivityIndicator?.stopAnimating()
loadingActivityIndicator?.isHidden = true
}
}
然后,UIViewController
子类只需为activityIndicator
定义自己的ivar,例如
class ViewController: UIViewController, LoadingIndicatorProtocol {
var loadingActivityIndicator: UIActivityIndicatorView?
override viewDidLoad() {
super.viewDidLoad()
addLoadingIndicator()
}
...
}
另一种方法是通过associated objects和objc_getAssociatedObject
使用objc_setAssociatedObject
来实现存储属性的行为:
protocol LoadingIndicatorProtocol {
var loadingActivityIndicator: UIActivityIndicatorView { get }
}
private var associatedObjectKey = 0
extension LoadingIndicatorProtocol {
var loadingActivityIndicator: UIActivityIndicatorView {
if let indicatorView = objc_getAssociatedObject(self, &associatedObjectKey) as? UIActivityIndicatorView {
return indicatorView
}
let indicator = UIActivityIndicatorView(style: .gray)
indicator.hidesWhenStopped = true
indicator.style = .whiteLarge
indicator.color = .red
indicator.backgroundColor = .gray
indicator.translatesAutoresizingMaskIntoConstraints = false
objc_setAssociatedObject(self, &associatedObjectKey, indicator, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return indicator
}
func showLoadingIndicator() {
loadingActivityIndicator.startAnimating()
loadingActivityIndicator.isHidden = false
}
func hideLoadingIndicator() {
loadingActivityIndicator.stopAnimating()
loadingActivityIndicator.isHidden = true
}
}