草庐IT

ios - 子类化时避免违反 LSP

coder 2023-09-09 原文

在 objective-C 中,我可以子类化一个 View Controller ,如下所示。

class KeyboardObserverViewController: UIViewController {

    var tableView: UITableView?

    init() {
        super.init(nibName: nil, bundle: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(KeyboardObserverViewController.keyboardDidShow(_:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(KeyboardObserverViewController.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func keyboardDidShow(_ notification: Notification) {
        let rect = ((notification as NSNotification).userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        if let tableView = tableView {
            let insets = UIEdgeInsetsMake(tableView.contentInset.top, 0, rect.height, 0)
            tableView.contentInset = insets
            tableView.scrollIndicatorInsets = insets
        }
    }

    func keyboardWillHide(_ notification: Notification) {
        if let tableView = tableView {
            let insets = UIEdgeInsetsMake(tableView.contentInset.top, 0, 0, 0)
            UIView.animate(withDuration: 0.3, animations: {
                tableView.contentInset = insets
                tableView.scrollIndicatorInsets = insets
            })
        }
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }
}

并覆盖 TableView 变量并返回一个更专业的 TableView (即 UITableView 的子类)。然后我可以在需要时转换 TableView 变量。在 Swift 中,这有点棘手,如 this post 中所述。 .

那么你如何将这个 View Controller 子类化,创建一个具有更多特殊性的类,同时避免 LSP violation .或者子类化一个 View Controller (以及子类化它的变量)太棘手了?

编辑:关于我的帖子可能类似于 this post 的建议- 我更专注于处理代码重复而不是类与结构。

澄清:我专门在 Swift 中寻找一种方法(或最佳实践),它允许我一次编写此代码,并将其用于各种使用其 CustomTableView 实例的 View Controller 子类拥有。

最佳答案

以下情况如何:

1 一些用于获取 UITableView 子类的通用协议(protocol)。

protocol TableViewContainer {
  associatedtype T : UITableView
  var tableView : T? { get }
}

2 然后是观察者协议(protocol):

protocol KeyboardEventsObserver {
  func registerKeyboardEvents()
  func keyboardDidShow(_ notification: Notification)
  func keyboardWillHide(_ notification: Notification)
}

3 然后当观察者也是一个 TableView 容器时的扩展。所以我们可以重用代码:

extension KeyboardEventsObserver where Self : TableViewContainer {

  func registerKeyboardEvents() {
    NotificationCenter.default.addObserver(forName: .UIKeyboardDidShow, object: nil, queue: nil) {
      notification in
      self.keyboardDidShow(notification)
    }
    NotificationCenter.default.addObserver(forName: .UIKeyboardWillHide, object: nil, queue: nil) {
      notification in
      self.keyboardWillHide(notification)
    }
  }

  func keyboardDidShow(_ notification: Notification) {
    let rect = ((notification as NSNotification).userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
    if let tableView = tableView {
      let insets = UIEdgeInsetsMake(tableView.contentInset.top, 0, rect.height, 0)
      tableView.contentInset = insets
      tableView.scrollIndicatorInsets = insets
      tableView.backgroundColor = UIColor.red
    }
  }

  func keyboardWillHide(_ notification: Notification) {
    if let tableView = tableView {
      let insets = UIEdgeInsetsMake(tableView.contentInset.top, 0, 0, 0)
      UIView.animate(withDuration: 0.3, animations: {
        tableView.contentInset = insets
        tableView.scrollIndicatorInsets = insets
      })
      tableView.backgroundColor = UIColor.green
    }
  }
}

4 最后,我们只是将我们需要该功能的 UIViewController 子类化。请注意,tableView 可以是 UITableView 的任何子类。

class MyCustomTableView : UITableView {

}

class SomeController : UIViewController, KeyboardEventsObserver, TableViewContainer {

  @IBOutlet var tableView: MyCustomTableView?

  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    registerKeyboardEvents()
  }

  override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    NotificationCenter.default.removeObserver(self)
  }
}

关于ios - 子类化时避免违反 LSP,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39827332/

有关ios - 子类化时避免违反 LSP的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  3. Ruby——嵌套类和子类是一回事吗? - 2

    下面例子中的Nested和Child有什么区别?是否只是同一事物的不同语法?classParentclassNested...endendclassChild 最佳答案 不,它们是不同的。嵌套:Computer之外的“Processor”类只能作为Computer::Processor访问。嵌套为内部类(namespace)提供上下文。对于ruby​​解释器Computer和Computer::Processor只是两个独立的类。classComputerclassProcessor#Tocreateanobjectforthisc

  4. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  5. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  6. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  7. Ruby - 如何处理子类意外覆盖父类(super class)私有(private)字段的问题? - 2

    假设您编写了一个类Sup,我决定将其扩展为SubSup。我不仅需要了解你发布的接口(interface),还需要了解你的私有(private)字段。见证这次失败:classSupdefinitialize@privateField="fromsup"enddefgetXreturn@privateFieldendendclassSub问题是,解决这个问题的正确方法是什么?看起来子类应该能够使用它想要的任何字段而不会弄乱父类(superclass)。编辑:equivalentexampleinJava返回"fromSup",这也是它应该产生的答案。 最佳答案

  8. ruby - 为什么不能使用类IO的实例方法noecho? - 2

    print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上

  9. ruby - 在 ruby​​ 中使用正确的异常子类 - 2

    我可以访问ruby​​的异常层次结构(它在镐和蜂鸟中都提到过),但我不确定使用哪个异常,因为我没有找到关于每个术语含义的任何信息。使用正确的异常类重要吗? 最佳答案 创建您自己的异常时很重要。一个重要的警告是,继承自Exception而不是StandardError(常见错误)的异常不会被rescue捕获(没有任何参数)。 关于ruby-在ruby​​中使用正确的异常子类,我们在StackOverflow上找到一个类似的问题: https://stackove

  10. ruby - 在 Ruby 中为变量赋值时如何避免控制台输出 - 2

    赋值时是否可以避免这种影响:irb(main):584:0>a=true=>trueirb(main):584:0>我有一个代码有很多赋值,当我试图测试它时,由于所有这些返回值,我看不到结果:truefalsetruefalsetruetrue.. 最佳答案 您可以启动irb或附加--noecho选项的控制台。$irb--noecho2.0.0p353:001>true2.0.0p353:002>否则,如果控制台由另一个进程启动,只需设置conf.echo=false$irb2.0.0p353:001>true=>true2.0.0

随机推荐