在objective-C中,我可以将视图控制器子类化如下。
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)
}
}
覆盖表视图变量并返回更专业的表视图(即UITableView的子类)。然后,我可以在需要时将表视图变量转换为。在Swift中,这有点棘手,如this post中所述。
那么你将如何继承这个视图控制器,创建一个具有更多专业性的类,同时避免使用LSP violation。或者是继承视图控制器(并对其变量进行子类化),这太棘手了?
编辑:关于我的帖子可能与this post类似的建议 - 我更关注处理代码重复而不是类与结构。
澄清:我特意在Swift中寻找一种方法(或最佳实践),允许我编写一次这个代码,并在各种视图控制器子类中使用它,这些子类使用他们的CustomTableView实例自己的。
答案 0 :(得分:2)
以下内容如何:
1获取UITableView
子类的一些通用协议。
protocol TableViewContainer {
associatedtype T : UITableView
var tableView : T? { get }
}
2然后是Observer的协议:
protocol KeyboardEventsObserver {
func registerKeyboardEvents()
func keyboardDidShow(_ notification: Notification)
func keyboardWillHide(_ notification: Notification)
}
3然后是观察者也是表视图容器的扩展名。所以我们可以重用代码:
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)
}
}
答案 1 :(得分:1)
如果你很高兴在Objective-C中的控制器子类中转换表视图,你可以在Swift中同样这样做:
import Foundation
class Table : NSObject {
var inset: CGFloat = 0
}
class NotifiedController : NSObject {
var table: Table?
override init() {}
func didGetNotification() {
self.table?.inset = 10
}
}
class WishingTable : Table {
var twinklingStarCount: Int = 0
}
class WishingController : NotifiedController {
override init() {
super.init()
self.table = WishingTable()
}
func makeAWish() {
if let wishingTable = self.table as? WishingTable {
wishingTable.twinklingStarCount += 1
}
}
}
这根本不需要覆盖属性。
答案 2 :(得分:0)
要支持子类化,请尝试以下方法:
class KeyboardObserverViewController: UIViewController {
func keyboardAvoidingTableView() -> UITableView? {
return nil
}
...
}
class SomeViewController: KeyboardObserverViewController {
var tableView: MyTableView? // <- your table view as usual
override func keyboardAvoidingTableView() -> UITableView? {
return self.tableView
}
}
在一般情况下,你可以反过来做,我更喜欢这个作为你提到的子类化问题的一般解决方法:
class KeyboardObserverViewController: UIViewController {
var tableView: UITableView?
...
}
class SomeViewController: KeyboardObserverViewController {
init() {
....
self.tableView = MyTableView()
}
var myTableView: MyTableView? {
return self.tableView as? MyTableView
}
}
虽然在这种特殊情况下,我看到keyboardAvoidingTableView
(或代码中的tableView)不是作为存储视图引用的变量,而是作为&#34;用键盘移动的表视图&#34; 。更重要的是,您可以将其设为UIScrollView,而不是在您的应用中假设更广泛的情况:
func keyboardAvoidingScrollView() -> UIScrollView?
答案 3 :(得分:0)
作为使用通用协议的替代方法,您可以直接在类定义中使用泛型。您仍然无法覆盖view
属性:与协议一样,您必须在基类中提供额外的,通用类型的属性,但这实际上是唯一的额外开销代码。
这些是示例基类(从我的其他答案重用)。控制器类是参数化的,第二个(计算的)属性提供表作为正确的子类类型。
import Foundation
class Table : NSObject {
var inset: CGFloat = 0
}
class NotifiedController<SpecializedTable : Table> : NSObject {
var table: Table?
// Giving this a good name is a bit tricky...
var specializedTable: SpecializedTable? {
return self.table as? SpecializedTable
}
func didGetNotification() {
self.table?.inset = 10
}
}
现在可以定义子类:
class WishingTable : Table {
var twinklingStarCount: Int = 0
}
class WishingController : NotifiedController<WishingTable> {
override init() {
super.init()
self.table = WishingTable()
}
func makeAWish() {
self.specializedTable?.twinklingStarCount += 1
}
}
请注意,控制器子类在继承子句中声明其专门化。
一个警告是,这些类,因为它们使用Swift泛型,现在在Objective-C代码中不可用。但是通用协议解决方案也是如此。