子类和超类的快速约束协议

问题描述 投票:1回答:4

我想为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来调用showLoadingViewhideLoadingView。问题是我想对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。”

swift protocols swift-protocols
4个回答
1
投票

问题

因此,仅当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%地满足您的需求,但是从实际的角度来看,它应该为您提供实现所需功能的工具。


0
投票

我会采用这种方法:

  1. 创建UIView类,
  2. 设置视图
  3. 声明一个共享对象。
  4. 显示视图的功能
  5. 删除视图的功能。然后在视图控制器中将其称为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()
    }
    
    }
    

0
投票

对于这种特定情况,装饰器将更好地工作,并导致更好的设计:

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方法允许您决定要接收哪些视图实例。功能。


0
投票

我将通过以下方法使用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
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.