我想为UIViewCntroller和UIView实现自己的HUD,所以我做到了:
protocol ViewHudProtocol {
func showLoadingView()
func hideLoadingView()
}
extension ViewHudProtocol where Self: UIView {
func showLoadingView() { //Show HUD by adding a custom UIView to self.}
}
func hideLoadingView() {
}
}
现在,我可以轻松地在任何UIView上采用ViewHudProtocol来调用showLoadingView
和hideLoadingView
。问题是我想对UIViewController使用相同的协议,所以我这样做了:
extension ViewHudProtocol where Self: UIViewController {
func showLoadingView() {
self.view.showLoadingView() //Error: UIView has no member showLoadingView
}
func hideLoadingView() {
self.view.hideLoadingView() //Error: UIView has no member hideLoadingView
}
}
我同意UIView尚未采用该协议的错误。所以我做到了:
extension UIView: ViewHudProtocol {}
并且有效。有一个更好的方法吗?我的意思是用ViewHudProtocol
扩展每个视图感觉不对,因为并不是所有人都将使用它。如果我可以做类似的事情,“如果UIViewController要求,则仅对UIView隐式采用ViewHudProtocol。否则,可以在需要时在任何UIView上手动采用ViewHUDProtocol。”
因此,仅当UIViewController
属性符合ViewHudProtocol
时,才想约束UIViewController.view
与协议ViewHudProtocol
的符合。
恐怕这是不可能的。
让我们更好地解决您的问题
您有2种类型(UIView和UIViewController,并且您想要添加到两个相同的功能中]
func showLoadingView()
func hideLoadingView()
[这种情况在某种程度上类似于Mick West在Tony Hawks系列Mick West的开发过程中所面临的情况,并且在其文章Evolve your hierarchy中描述了一种优雅的解决方案。
我们可以将这种方法应用于您的问题,这就是解决方案
struct HudViewComponent {
let view: UIView
private let hud: UIView
init(view: UIView) {
self.view = view
self.hud = UIView(frame: view.frame)
self.hud.isHidden = true
self.view.addSubview(hud)
}
func showLoadingView() {
self.hud.isHidden = false
}
func hideLoadingView() {
self.hud.isHidden = true
}
}
protocol HasHudViewComponent {
var hidViewComponent: HudViewComponent { get }
}
extension HasHudViewComponent {
func showLoadingView() {
hidViewComponent.showLoadingView()
}
func hideLoadingView() {
hidViewComponent.hideLoadingView()
}
}
就是这样,现在您可以将平视功能添加到符合HasHudViewComponent
的任何Type。
class SomeView: UIView, HasHudViewComponent {
lazy var hidViewComponent: HudViewComponent = { return HudViewComponent(view: self) }()
}
或
class MyViewController: UIViewController, HasHudViewComponent {
lazy var hidViewComponent: HudViewComponent = { return HudViewComponent(view: self.view) }()
}
您可以看到,想法是根据组件进行思考。您可以使用平视功能来构建组件(HudViewComponent
)。该组件仅要求最低要求:它需要一个UIView。
接下来,您定义HasHudViewComponent
,它声明当前类型具有a HudViewComponent
属性。
最后,您可以将平视功能添加到任何具有视图的类型(UIView,UIViewController等),只需将您的类型与HasHudViewComponent
保持一致即可。
您问了一个有趣的问题,我知道这不能100%地满足您的需求,但是从实际的角度来看,它应该为您提供实现所需功能的工具。
我会采用这种方法:
删除视图的功能。然后在视图控制器中将其称为IndicatorView.shared.show()
,IndicatorView.shared.hide()
import Foundation
import UIKit
import Lottie
class IndicatorView : UIView {
static let shared = IndicatorView()
var loadingAnimation : AnimationView = {
let lottieView = AnimationView()
lottieView.translatesAutoresizingMaskIntoConstraints = false
lottieView.layer.masksToBounds = true
return lottieView
}()
var loadingLabel : UILabel = {
let label = UILabel()
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont(name: "SegoeUI", size: 12)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = false
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func show() {
setupLoadingView()
self.alpha = 0
UIView.animate(withDuration: 0.5, animations: {
self.isHidden = false
self.alpha = 1
}, completion: nil)
applyLottieAnimation()
}
public func hide() {
self.alpha = 1
UIView.animate(withDuration: 0.5, animations: {
self.alpha = 0
}, completion: { _ in
self.isHidden = true
self.removeFromSuperview()
}) }
private func setupLoadingView() {
let controller = UIApplication.shared.keyWindow!.rootViewController!
controller.view.addSubview(self)
//setup your views here
self.setNeedsLayout()
self.reloadInputViews()
}
}
对于这种特定情况,装饰器将更好地工作,并导致更好的设计:
final class HUDDecorator {
private let view: UIView
init(_ view: UIView) {
self.view = view
}
func showLoadingView() {
// add the spinner
}
func hideLoadingView() {
// remove the spinner
}
}
然后使用装饰器就像为其声明属性一样容易:
class MyViewController: UIViewController {
lazy var hudDecorator = HUDDecorator(view)
}
这将允许任何控制器通过简单地公开此属性来决定是否要支持显示/隐藏加载视图。
协议对于诸如增强UI组件的外观之类的简单任务而言过于侵入性,它们的缺点是强制某个类的所有视图公开协议功能,而Decorator方法允许您决定要接收哪些视图实例。功能。
我将通过以下方法使用associatedtype
解决此问题,该方法仅为所需的视图和/或控制器定义(在Xcode 11.2 / swift 5.1中测试):
protocol ViewHudProtocol {
associatedtype Content : ViewHudProtocol
var content: Self.Content { get }
func showLoadingView()
func hideLoadingView()
}
extension ViewHudProtocol where Self: UIView {
var content: some ViewHudProtocol {
return self
}
func showLoadingView() { //Show HUD by adding a custom UIView to self.}
}
func hideLoadingView() {
}
}
extension ViewHudProtocol where Self: UIViewController {
func showLoadingView() {
self.content.showLoadingView() //NO Error
}
func hideLoadingView() {
self.content.hideLoadingView() //NO Error
}
}
//Usage
extension UITableView: ViewHudProtocol { // only for specific view
}
extension UITableViewController: ViewHudProtocol { // only for specific controller
var content: some ViewHudProtocol {
return self.tableView
}
}