7

我有一个 13 行的函数,它在我的应用程序中的每个 ViewController 中重复,整个项目总共有 690 行代码!

/// Adds Menu Button
func addMenuButton() {
    let menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
    let menuImage = UIImage(named: "MenuWhite")
    menuButton.setImage(menuImage, for: .normal)

    menuButton.addTarget(self, action: #selector(menuTappedAction), for: .touchDown)
    self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: menuButton)
}
/// Launches the MenuViewController
@objc func menuTappedAction() {
    coordinator?.openMenu()
}

为了使 menuTappedAction 函数起作用,我必须像这样声明一个弱变量:

extension UIViewController {

weak var coordinator: MainCoordinator?

但是通过这样做,我得到了错误Extensions must not contain stored properties 到目前为止我尝试过的内容:

1)删除weak关键字会导致我所有的应用程序发生冲突。2)以这种方式声明:

weak var coordinator: MainCoordinator?
extension UIViewController {

将使错误静音,但协调器不会执行任何操作。任何建议如何解决这个问题?

4

6 回答 6

7

您可以将您的addMenuButton()功能移动到具有协议扩展的协议。例如:

@objc protocol Coordinated: class {
    var coordinator: MainCoordinator? { get set }
    @objc func menuTappedAction()
}

extension Coordinated where Self: UIViewController {
    func addMenuButton() {
        let menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
        let menuImage = UIImage(named: "MenuWhite")
        menuButton.setImage(menuImage, for: .normal)

        menuButton.addTarget(self, action: #selector(menuTappedAction), for: .touchDown)
        self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: menuButton)
    }
}

不幸的是,您不能@objc向类扩展添加方法(请参阅:this stackoverflow question),因此您仍然必须像这样设置视图控制器:

class SomeViewController: UIViewController, Coordinated {
    weak var coordinator: MainCoordinator?
    /// Launches the MenuViewController
    @objc func menuTappedAction() {
        coordinator?.openMenu()
    }
}

它将为您节省一些代码,并允许您重构更大的函数addMenuButton()。希望这可以帮助!

于 2019-09-30T21:57:07.020 回答
5

为了让它在扩展中工作,你必须使它像这样计算属性: -

extension ViewController {

   // Make it computed property
    weak var coordinator: MainCoordinator? {
        return MainCoordinator()
    }

}

于 2019-09-15T14:11:25.533 回答
4

您可以使用 objc 关联对象。

extension UIViewController {
    private struct Keys {
        static var coordinator = "coordinator_key"
    }

    private class Weak<V: AnyObject> {
        weak var value: V?

        init?(_ value: V?) {
            guard value != nil else { return nil }
            self.value = value
        }
    }

    var coordinator: Coordinator? {
        get { (objc_getAssociatedObject(self, &Keys.coordinator) as? Weak<Coordinator>)?.value }
        set { objc_setAssociatedObject(self, &Keys.coordinator, Weak(newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
    }
}
于 2019-10-01T08:12:33.097 回答
2

发生这种情况是因为 anextension不是类,因此它不能包含存储的属性。即使它们是weak财产。

考虑到这一点,您有两个主要选择:

  1. 快捷方式:协议+协议扩展
  2. 讨厌的 objc 方式:关联对象

选项 1:使用协议和协议扩展:

1.1。声明你的协议

protocol CoordinatorProtocol: class {
    var coordinator: MainCoordinator? { get set }
    func menuTappedAction()
}

1.2. 创建一个协议扩展,以便您可以预先实现该addMenuButton()方法

extension CoordinatorProtocol where Self: UIViewController {
    func menuTappedAction() {
        // Do your stuff here
    }
}

1.3. weak var coordinator: MainCoordinator?在将采用此协议的类中声明。不幸的是,你不能跳过这个

class SomeViewController: UIViewController, CoordinatorProtocol {
    weak var coordinator: MainCoordinator?
}

选项 2:使用 objc 关联对象(不推荐)

extension UIViewController {
    private struct Keys {
        static var coordinator = "coordinator_key"
    }

    public var coordinator: Coordinator? {
        get { objc_getAssociatedObject(self, &Keys.coordinator) as? Coordinator }
        set { objc_setAssociatedObject(self, &Keys.coordinator, newValue, .OBJC_ASSOCIATION_ASSIGN) }
    }
}
于 2019-10-02T21:07:12.263 回答
0

使用 aNSMapTable为您的扩展创建一个状态容器,但请确保您指定为键使用弱引用。

创建一个要在其中存储状态的类。让我们调用它ExtensionState,然后在扩展文件中创建一个映射作为私有字段。

private var extensionStateMap: NSMapTable<TypeBeingExtended, ExtensionState> = NSMapTable.weakToStrongObjects()

然后你的扩展可以是这样的。

extension TypeBeingExtended {
    private func getExtensionState() -> ExtensionState {
        var state = extensionStateMap.object(forKey: self)

        if state == nil {
            state = ExtensionState()
            extensionStateMap.setObject(state, forKey: self)
        }

        return state
    }

    func toggleFlag() {
        var state = getExtensionState()
        state.flag = !state.flag
    }
}

这适用于 iOS 和 macOS 开发,但不适用于服务器端 Swift,因为没有NSMapTable

于 2021-03-29T13:14:55.763 回答
0

您可以通过子类化来做到这一点

class CustomVC:UIViewController {

    weak var coordinator: MainCoordinator?

    func addMenuButton() {
        let menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
        let menuImage = UIImage(named: "MenuWhite")
        menuButton.setImage(menuImage, for: .normal)

        menuButton.addTarget(self, action: #selector(menuTappedAction), for: .touchDown)
        self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: menuButton)
    }
    /// Launches the MenuViewController
    @objc func menuTappedAction() {
        coordinator?.openMenu()
    }

}

class MainCoordinator {

    func openMenu() {

    }
}


class ViewController: CustomVC {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

}
于 2019-09-15T13:21:06.247 回答