matsuda
12/26/2016 - 12:09 AM

Badge.swift

/*
 enumに適用することを想定
 enum以外では未確認
 */

///
/// MARK: - ResourceBundle
///
protocol ResourceBundle {
    associatedtype T
    static var bundleName: String { get }
    static func value(forKey key: String) -> T
    func value(forKey key: String) -> T
}

extension ResourceBundle {
    static func value(forKey key: String) -> T {
        let path = bundlePath()!
        let dict = NSDictionary(contentsOfFile: path)!
        return dict[key] as! T
    }

    func value(forKey key: String) -> T {
        return type(of: self).value(forKey: key)
    }

    static func bundlePath() -> String? {
        guard let path = Bundle.main.path(forResource: bundleName, ofType: "bundle") else { return nil }
        return Bundle(path: path)?.path(forResource: "\(self)", ofType: "plist")
        // return NSBundle.resourceBundle(bundleName)?.pathForResourcePlist("\(self)")
        // return bundle().pathForResource("\(self)", ofType: "plist")
    }

    /*
    static func bundle() -> NSBundle {
        let path = NSBundle.mainBundle().pathForResource(bundleName, ofType: "bundle")!
        return NSBundle(path: path)!
    }
     */
}


///
/// MARK: - RawStringEnumerable
///
/// @see RawRepresentable
///
protocol RawStringEnumerable {
    var rawValue: String { get }
    init?(rawValue: String)
}

extension ResourceBundle where Self: RawStringEnumerable {
    var value: T {
        return value(forKey: rawValue)
    }
}


// example


//
// MARK: - MyServiceResource
//
protocol MyServiceResourceBundle: ResourceBundle {}
extension MyServiceResourceBundle {
    static var bundleName: String {
        return "MyService"
    }
}

//
// MARK: - Resource
//
struct Resource {}

extension Resource {
    enum URLs: String, MyServiceResourceBundle, RawStringEnumerable {
        typealias T = String

        case login

        /*
#if DEBUG || ADHOC
        static func value(forKey key: String) -> String {
            let path = bundlePath()!
            let dict = NSDictionary(contentsOfFile: path)!
            let value = dict[key] as! T

            let pattern = "(https?://)([^/]+?)(/.*)"
            let scheme = Network.sharedNetwork.URL.scheme!
            let host = Network.sharedNetwork.URL.host!
            var hostWithPort: String!
            if let port = Network.sharedNetwork.URL.port {
//                hostWithPort = "$1\(host):\(port)$3"
                hostWithPort = "\(scheme)://\(host):\(port)$3"
            } else {
//                hostWithPort = "$1\(host)$3"
                hostWithPort = "\(scheme)://\(host)$3"
            }
            let str = value.replacingOccurrences(
                of: pattern,
                with: hostWithPort,
                options: [.regularExpression],
                range: nil
            )
            return str
        }
#endif
        */
    }
}


// alias
typealias R = Resource
// MARK: - app version

func appVersion() -> String {
    return Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String
}

// MARK: - NSBundle

func bundlePath(path: String) -> String? {
    let resourcePath = Bundle.main.resourcePath as NSString?
    return resourcePath?.appendingPathComponent(path)
}

// MARK: - app launguage

func currentLaunguage() -> String {
    if let languageAndCountry = Locale.preferredLanguages.first,
        let language = languageAndCountry.components(separatedBy: "-").first
    {
        return language
    }
    return "en"
}

// MARK: - localize wrapper

func AppLocalizedStringFromTable(key: String, tableName: String? = nil, comment: String) -> String {
    guard
        let path = Bundle.main.path(forResource: currentLaunguage(), ofType: "lproj"),
        let bundle = Bundle(path: path),
        let tableName = tableName else {
            return NSLocalizedString(key, comment: comment)
    }
    return bundle.localizedString(forKey: key, value: nil, table: tableName)
    // return NSLocalizedString(key, tableName: tableName, bundle: bundle, comment: comment)
}
///
/// MARK: - BadgeButton
///
class BadgeButton: UIButton {
    let badgeLabel: BadgeLabel = BadgeLabel()

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initialize()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        initialize()
    }

    convenience init(badge: String?) {
        self.init(frame: .zero)
        initialize()
        badgeLabel.text = badge
    }

    private func initialize() {
        addSubview(badgeLabel)
        badgeLabel.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint(item: badgeLabel, attribute: .centerX, relatedBy: .equal,
                           toItem: self, attribute: .centerX,
                           multiplier: 1, constant: 6).isActive = true
        NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal,
                           toItem: badgeLabel, attribute: .centerY,
                           multiplier: 1, constant: 6).isActive = true
    }
}


///
/// MARK: - BadgeLabel
///
class BadgeLabel: UILabel {
    var badgeColor: UIColor = UIColor.red

    override var text: String? {
        didSet {
            guard let text = self.text else {
                isHidden = true
                return
            }
            if text.isEmpty {
                isHidden = true
                return
            }
            if let count = Int(text), count == 0 {
                isHidden = true
                return
            }
            isHidden = false
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        initialize()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initialize()
    }

    init(badge: String?, color: UIColor = .red) {
        self.init()
        initialize()
        badgeColor = color
        text = badge
    }

    private func initialize() {
        textColor = UIColor.white
        backgroundColor = UIColor.clear
        textAlignment = .center
    }

    private let minimumSize: CGSize = CGSize(width: 16, height: 16)
    private let margin: CGFloat = 1

    override func draw(_ rect: CGRect) {
        badgeColor.setFill()
        let path = UIBezierPath()
        let unit = rect.height / 2
        var point = CGPoint(x: rect.minX + unit, y: rect.minY)
        path.move(to: point)
        point.x = rect.maxX - unit
        path.addLine(to: point)
        point.y = rect.midY
        path.addArc(withCenter: point, radius: unit,
                    startAngle: CGFloat(-M_PI / 2),
                    endAngle: CGFloat(M_PI / 2),
                    clockwise: true)
        point.y = rect.maxY
        point.x = rect.minX + unit
        path.addLine(to: point)
        point.y = rect.midY
        path.addArc(withCenter: point, radius: unit,
                    startAngle: CGFloat(M_PI / 2),
                    endAngle: CGFloat(-M_PI / 2),
                    clockwise: true)
        path.close()
        path.fill()
        super.draw(rect)
    }

    /*
    override func drawText(in rect: CGRect) {
        let insets = UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin)
        let rect = UIEdgeInsetsInsetRect(rect, insets)
        super.drawText(in: rect)
    }
    */

    override var intrinsicContentSize: CGSize {
        var size = super.intrinsicContentSize
        if size.height < minimumSize.height {
            size.height = minimumSize.height
        }
        size.width += margin * 2
        size.height += margin * 2
        if size.width < size.height {
            size.width = size.height
        }
        return size
    }
}


///
/// MARK: - BadgeBarButtonItem
///
class BadgeBarButtonItem: UIBarButtonItem {
    private var badgeButton: BadgeButton {
        return customView as! BadgeButton
    }

    var badgeLabel: BadgeLabel {
        return badgeButton.badgeLabel
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        fatalError("Not supported \(#function).")
    }

    init(badge: String?, target: Any?, action: Selector) {
        let button = BadgeButton(badge: badge)
        button.isExclusiveTouch = true
        button.setImage(UIImage(named: "btn_badge"), for: .normal)
        button.addTarget(target, action: action, for: .touchUpInside)
        button.frame = CGRect(x: 0, y: 0, width: 45, height: 30)
        super.init()
        customView = button
        addObserve()
    }

    deinit {
        removeObserve()
    }

    private func addObserve() {
        NotificationCenter.default.addObserver(self, selector: #selector(didChangeBadgeCount(notification:)), name: Notification.Name("DidChangeBadgeCount"), object: nil)
    }

    private func removeObserve() {
        NotificationCenter.default.removeObserver(self, name: Notification.Name("DidChangeBadgeCount"), object: nil)
    }

    func didChangeBadgeCount(notification: Notification) {
        guard let userInfo = notification.userInfo,
            let count = userInfo["BadgeCount"] as? Int else {
                return
        }
        badgeLabel.text = BadgeBarButtonItem.badgeCountText(count: count)
    }

    class func badgeCountText(count: Int) -> String {
        if count > 30 {
            return "30+"
        } else {
            return String(count)
        }
    }
}