代码之家  ›  专栏  ›  技术社区  ›  Alok C

用swift进行课堂到协议对话

  •  1
  • Alok C  · 技术社区  · 6 年前

    我有一个很重的uiviewcontroller的basicvc类子类,我正试图将其转换为vcprotocol。

    是basicvc像上帝课一样做所有的工作。我想把它作为vcprotocol侵入。

    我想做的是分心。并非所有的视图控制器都需要显示警报视图或网络未连接消息。

    例如,我在协议扩展中创建了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
        }
    }
    

    我不知道该如何解决这个问题。因为我只能在协议中计算属性。 所以我把它们作为唯一的属性。 我的计划是使用协议扩展来提供默认的实现。

    关于如何解决这个问题的任何想法。

    1 回复  |  直到 6 年前
        1
  •  2
  •   Rob Md Fahim Faez Abir    6 年前

    这个 activityIndicator 是计算属性,因此每次引用计算属性时, get 将调用块。净效果是,正如所写,每次引用 活动指示器 ,您将获得 UIActivityIndicatorView 这显然不是你的意图。

    考虑你的 showLoadingIndicator :

    func showLoadingIndicator() {
        activityIndicator.startAnimating()
        activityIndicator.isHidden = false
    }
    

    第一行 startAnimating )将返回新的 活动指示器视图 第二行 isHidden )将再次返回。这两个都不是你添加到子视图中的那个。

    这个 活动指示器 真的应该实例化一次,而且只实例化一次。不幸的是,您可以在扩展中定义存储的属性,因此有几种方法:

    1. 你可以让 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
          }
      }
      

      然后一个 ui视图控制器 子类只需为 活动指示器 ,例如

      class ViewController: UIViewController, LoadingIndicatorProtocol {
          var loadingActivityIndicator: UIActivityIndicatorView?
      
          override viewDidLoad() {
              super.viewDidLoad()
      
              addLoadingIndicator()
          }
      
          ...
      }
      
    2. 另一种方法是 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
          }
      }