Skip to content

Instantly share code, notes, and snippets.

@ksm
Forked from BeauNouvelle/UIBarButtonItem Closure 1.swift
Last active February 10, 2025 00:00
Show Gist options
  • Save ksm/1e662f84e073cd227c31f80699d0c197 to your computer and use it in GitHub Desktop.
Save ksm/1e662f84e073cd227c31f80699d0c197 to your computer and use it in GitHub Desktop.
Slightly improved version of Beau Nouvelle's UIBarButtonItem closure extension. I hid all the helper classes within the extension and made them private, so as not to pollute the global namespace. Original article: https://medium.com/@BeauNouvelle/adding-a-closure-to-uibarbuttonitem-24dfc217fe72
import Foundation
import UIKit
public extension UIBarButtonItem {
public typealias TargetClosure = (UIBarButtonItem) -> ()
public convenience init(title: String?, style: UIBarButtonItem.Style = .plain, closure: @escaping TargetClosure) {
self.init(title: title, style: style, target: nil, action: nil)
targetClosure = closure
action = #selector(UIBarButtonItem.closureAction)
}
private struct AssociatedKeys {
static var targetClosure = "targetClosure"
}
private class ClosureWrapper: NSObject {
let closure: TargetClosure
init(_ closure: @escaping TargetClosure) {
self.closure = closure
}
}
private var targetClosure: TargetClosure? {
get {
guard let closureWrapper = objc_getAssociatedObject(self, &AssociatedKeys.targetClosure) as? ClosureWrapper else {
return nil
}
return closureWrapper.closure
}
set {
guard let newValue = newValue else {
return
}
objc_setAssociatedObject(self, &AssociatedKeys.targetClosure, ClosureWrapper(newValue), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
@objc private func closureAction() {
targetClosure?(self)
}
}
@Thomas1956
Copy link

This is a very useful extension. Because of some warnings I made some changes.

import UIKit
import ObjectiveC

public extension UIBarButtonItem {
    typealias TargetClosure = (UIBarButtonItem) -> Void
    
    // MARK: - Convenience-Inits mit Closure
    
    convenience init(
        title: String?,
        style: UIBarButtonItem.Style = .plain,
        closure: @escaping TargetClosure
    ) {
        self.init(title: title, style: style, target: nil, action: nil)
        targetClosure = closure
        action = #selector(closureAction)
    }

    convenience init(
        image: UIImage?,
        style: UIBarButtonItem.Style = .plain,
        closure: @escaping TargetClosure
    ) {
        self.init(image: image, style: style, target: nil, action: nil)
        targetClosure = closure
        action = #selector(closureAction)
    }
    
    // MARK: - Associated Object Key
    
    // Statt eines Strings verwenden wir einen statischen Key (UInt8),
    // damit der Zeiger stabil bleibt und keine UnsafeRawPointer-Warnung entsteht.
    private static var targetClosureKey: UInt8 = 0

    // MARK: - ClosureWrapper
    
    private class ClosureWrapper: NSObject {
        let closure: TargetClosure
        init(_ closure: @escaping TargetClosure) {
            self.closure = closure
        }
    }
    
    // MARK: - Computed Property: targetClosure
    
    private var targetClosure: TargetClosure? {
        get {
            guard let wrapper = objc_getAssociatedObject(self, &Self.targetClosureKey) as? ClosureWrapper else {
                return nil
            }
            return wrapper.closure
        }
        
        set {
            // Falls wir 'nil' setzen, wird die Assoziation entfernt:
            if let newValue = newValue {
                let wrapper = ClosureWrapper(newValue)
                objc_setAssociatedObject(
                    self,
                    &Self.targetClosureKey,
                    wrapper,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC
                )
            } else {
                objc_setAssociatedObject(
                    self,
                    &Self.targetClosureKey,
                    nil,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC
                )
            }
        }
    }
    
    // MARK: - Action
    
    @objc private func closureAction() {
        targetClosure?(self)
    }
}

@ksm
Copy link
Author

ksm commented Feb 10, 2025

Very cool! It's an old snippet that I'm sure has decayed quite a bit already. 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment