簡體   English   中英

Swift使用選擇器參數,如閉包

[英]Swift use selector argument like a closure

我只是想知道是否可以將一個函數傳遞給一個按鈕動作(通常是一個選擇器)。

例如,通常我會說:

UIBarButtonItem(title: "Press", style: .Done, target: self, action: "functionToCall")

func functionToCall() {
    // Do something
}

但我想知道是否可以做以下事情:

UIBarButtonItem(title: "Press", style: .Done, target: self, action: {
    // Do Something
})

我想要這樣做的原因是因為我的功能非常簡單,看起來它更整潔,更像Swift,因為它們強調它們放在封閉上。

這是Swift 3的更新解決方案。

class BlockBarButtonItem: UIBarButtonItem {
  private var actionHandler: ((Void) -> Void)?

  convenience init(title: String?, style: UIBarButtonItemStyle, actionHandler: ((Void) -> Void)?) {
    self.init(title: title, style: style, target: nil, action: #selector(barButtonItemPressed))
    self.target = self
    self.actionHandler = actionHandler
  }

  convenience init(image: UIImage?, style: UIBarButtonItemStyle, actionHandler: ((Void) -> Void)?) {
    self.init(image: image, style: style, target: nil, action: #selector(barButtonItemPressed))
    self.target = self
    self.actionHandler = actionHandler
  }

  func barButtonItemPressed(sender: UIBarButtonItem) {
    actionHandler?()
  }
}

這是一個沒有子類化的替代解決方案:

extension UIBarButtonItem {
    private struct AssociatedObject {
        static var key = "action_closure_key"
    }

    var actionClosure: (()->Void)? {
        get {
            return objc_getAssociatedObject(self, &AssociatedObject.key) as? ()->Void
        }
        set {
            objc_setAssociatedObject(self, &AssociatedObject.key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            target = self
            action = #selector(didTapButton(sender:))
        }
    }

    @objc func didTapButton(sender: Any) {
        actionClosure?()
    }
}

它依賴於Objective-C運行時的關聯對象來添加閉包/塊屬性。

設置時,它將目標更改為自身,並將選擇器指向一個新函數,如果存在,則調用該閉包。

這種情況,在任何時刻,你可以只設置actionClosure任何UIBarButtonItem ,並期望一切工作,這里有一個例子:

let button = UIBarButtonItem(
    barButtonSystemItem: .action,
    target: nil, action: nil
)
button.actionClosure = {
    print("hello")
}

Reddit上的一篇文章用自定義組件解釋了這個問題 - https://www.reddit.com/r/swift/comments/3fjzap/creating_button_action_programatically_using

要使用它,雖然我必須以編程方式而不是通過故事板添加Button。 我是這樣做的。

let tempVariableIWantToReference = "Classy"

navigationTitle.leftBarButtonItem = BlockBarButtonItem.init(
   title: "< Back", style: UIBarButtonItemStyle.Plain, 
   actionHandler: { () -> Void in
       print("Hey I'm in a closure")
       print("I can even reference temporary variables. \(self.tempVariableIWantToReference)!!!")
   })
  • 更新了Swift 5。
  • 實現具有目標的所有便利初始化程序。
  • 添加一個typealias的函數簽名清理語法。
  • UIBarButtonItem傳遞給動作處理程序以符合Cocoa約定。
import UIKit

class SwiftBarButtonItem: UIBarButtonItem {
    typealias ActionHandler = (UIBarButtonItem) -> Void

    private var actionHandler: ActionHandler?

    convenience init(image: UIImage?, style: UIBarButtonItem.Style, actionHandler: ActionHandler?) {
        self.init(image: image, style: style, target: nil, action: #selector(barButtonItemPressed(sender:)))
        target = self
        self.actionHandler = actionHandler
    }

    convenience init(title: String?, style: UIBarButtonItem.Style, actionHandler: ActionHandler?) {
        self.init(title: title, style: style, target: nil, action: #selector(barButtonItemPressed(sender:)))
        target = self
        self.actionHandler = actionHandler
    }

    convenience init(barButtonSystemItem systemItem: UIBarButtonItem.SystemItem, actionHandler: ActionHandler?) {
        self.init(barButtonSystemItem: systemItem, target: nil, action: #selector(barButtonItemPressed(sender:)))
        target = self
        self.actionHandler = actionHandler
    }

    @objc func barButtonItemPressed(sender: UIBarButtonItem) {
        actionHandler?(sender)
    }
}

不幸的是,Apple提供的初始化程序是不可能的。 它在后台運行的方式是使用反射,並且提供閉包是完全不同的,目前不支持。

您可能能夠通過一些黑客攻擊來創建自定義解決方案,或者Apple可能會在將來引入該解決方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM