-
-
Save amelnychuck/4cc03b252d2f8ebf8a8b05dade7d07e5 to your computer and use it in GitHub Desktop.
import UIKit | |
class TabBarViewController: UITabBarController { | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
} | |
// MARK: Keyboard Commands | |
override internal var keyCommands: [UIKeyCommand]? { | |
guard let controllers = viewControllers else { | |
return nil | |
} | |
var commands = [UIKeyCommand]() | |
for controller in controllers { | |
if let index = tabBar.items?.index(of: controller.tabBarItem) { | |
var pressedAction: Selector? | |
switch index { | |
case 0: | |
pressedAction = #selector(TabBarViewController.pressed1) | |
case 1: | |
pressedAction = #selector(TabBarViewController.pressed2) | |
case 2: | |
pressedAction = #selector(TabBarViewController.pressed3) | |
default: | |
break | |
} | |
if let action = pressedAction { | |
let command = UIKeyCommand(input: String(describing: index + 1), modifierFlags: .command, action: action, discoverabilityTitle: controller.tabBarItem.title!) | |
commands.append(command) | |
} | |
} | |
} | |
return commands | |
} | |
@objc func pressed1() { | |
selectedIndex = 0 | |
} | |
@objc func pressed2() { | |
selectedIndex = 1 | |
} | |
@objc func pressed3() { | |
selectedIndex = 2 | |
} | |
} |
I'd almost always advocate functional programming style and closures all the way, was expecting Apple to have block support for everything callback related when it was introduced in Objective-C way back.... even though that would require quite some code additions from their side...
However, with swift and bridging, they could've, possibly, added auto support / generation for closures-to-selectors, requiring no change on the massive amounts of API's from their side... (well, it is open-source, not sure they like auto-gen though...).
Nevertheless, the functionality you're asking for is still absolutely possible.
import UIKit
class TabBarViewController: UITabBarController {
// MARK: Setup
override func viewDidLoad() { super.viewDidLoad() }
// MARK: Keyboard Commands
var keyCommandsMap:[String:Int] = [:]
override internal var keyCommands: [UIKeyCommand]? {
guard let controllers = viewControllers else { return nil }
var commands = [UIKeyCommand]()
for controller in controllers {
guard let index = tabBar.items?.index(of: controller.tabBarItem) else { continue }
// ie. cmd-1 to go to tab nr 1
let input = "\(index + 1)"
let command = UIKeyCommand(
input: input,
modifierFlags: .command,
action: #selector(TabBarViewController.pressed),
discoverabilityTitle: controller.tabBarItem.title!
)
keyCommandsMap[input] = index
commands.append(command)
}
return commands
}
@objc func pressed(sender: UIKeyCommand) {
guard let index = keyCommandsMap[sender.input] else { return }
selectedIndex = index
}
}
Ps. guards is a great feature! This code is almost 50% less indented ;)
Explored some additional choises; (I find syntax to be quite intriguing... pondering about writing my own...:) )
Swift; more functional
import UIKit
class TabBarViewController: UITabBarController {
// MARK: Keyboard Commands
var keyCommandsMap:[String:Int] = [:]
override internal var keyCommands: [UIKeyCommand]? {
let getInput = { (i: Int) -> String in
let key = "\(i + 1)"
return keyCommandsMap[key] = i
}
return viewControllers?.map {$0.tabBarItem}.map {(
index: tabBar.items?.index(of: $0),
item: $0
)}.filter {$0.index != nil}.map {UIKeyCommand(
input: getInput($0.index!),
modifierFlags: .command,
action: #selector(TabBarViewController.pressed),
discoverabilityTitle: $0.item.title!
)}
}
@objc func pressed(sender: UIKeyCommand) {
guard let index = keyCommandsMap[sender.input] else { return }
selectedIndex = index
}
}
Swift; if closures were supported instead of selectors
import UIKit
class TabBarViewController: UITabBarController {
// MARK: Keyboard Commands
override internal var keyCommands: [UIKeyCommand]? {
return viewControllers?.map {$0.tabBarItem}.map {(
index: tabBar.items?.index(of: $0),
item: $0
)}.filter {$0.index != nil}.map {UIKeyCommand(
input: "\($0.index! + 1)",
modifierFlags: .command,
discoverabilityTitle: $0.item.title!
) { [weak self] in self.selectedIndex = $0.index! }}
}
}
LiteScript; Superset of ES7 JavaScript
import { UITabBarController, UIKeyCommand } from 'UIKit'
class TabBarViewController extends UITabBarController
keyCommands() -get>
this.viewControllers?.map(v-> v.tabBarItem)
.map(item-> ({item, index: tabBar.items?.indexOf(item)}))
.filter(({index})-> index)
.map(({index, item})-> new UIKeyCommand({
input: index+1+''
modifierFlags: 'command'
discoverabilityTitle: item.title
action: ()=> this.selectedIndex = index
})
Custom syntax; direct translation
import from UIKit: UITabBarController, UIKeyCommand
TabBarViewController inherits UITabBarController
keyCommands: viewControllers?
| map: .tabBarItem
| map as item: {item, index: tabBar.items?.indexOf item}
| filter: .index?
| map: UIKeyCommand
input: .index+1
modifiers: :command
title: .item.title
on action: selectedIndex= .index
Custom syntax; how I think I would like to write it
import from UIKit: UITabBarController, UIKeyCommand
TabBarViewController inherits UITabBarController
keyCommands: if viewControllers: iterate them:
item: its tabBarItem
index: tabBar.items?.indexOf item
skip if !index
yield UIKeyCommand
input: index+1
modifiers: :command
title: item.title
on action: selectedIndex= index
If we were able to use closures, we could instead turn
selectedIndex = 0
,selectedIndex = 1
, andselectedIndex = 2
in each corresponding method, to just beselectedIndex = self.viewControllers?.index(of: controller)
—GREATLY simplifying the code, and making all arguments of UIKeyCommand possible to extract from context so if I wanted to add a 4th item to my tab bar, I wouldn't need to change or add a single line of code.