Извините, это слишком поздно, и вы, вероятно, уже реализовали что-то еще, но после ДНЕЙ исследований и проб и ошибок у меня наконец-то есть над чем поработать.
Многие разработчики имеют действительно продвинутый пользовательский интерфейс в своих приложениях и используют множество настраиваемых всплывающих окон и контроллеров, которые обеспечивают уникальный вид их приложений, а затем вынуждены использовать стандартный пользовательский интерфейс контроллера AirPlay, который полностью выводит пользователя из работы с приложениями и может быть полным бельмом на глазу.
Другие хотят лучшего UX; т.е. Вместо того, чтобы разделять меню, в которые люди могут передавать свои текущие медиафайлы (одно для AirPlay и одно для всего остального), они хотят объединить два меню, чтобы у пользователя был действительно отличный опыт.
К сожалению, используя доступный сегодня общедоступный API, вы действительно ничего не можете сделать. Spotify пытается заставить его работать в своем приложении, и, хотя он лучше большинства подходов, все же оставляет желать лучшего.
Однако, если по какой-то причине вы не хотите выпускать свое приложение в магазине приложений, у вас есть хороший обфускатор кода, который может скрыть ваши вызовы функций из Apple (что очень маловероятно), или вы хотите распространить свое приложение в другом магазине (Cydia и т. д.) для вас есть вариант: частные API.
Многие люди уклоняются, когда слышат эти два слова, но как только вы привыкаете к формату, он действительно такой же, как обычное программирование в Какао за вычетом завершения кода Xcode.
Хватит болтать и просто ДАЖИ ДЕ КОДЕЗ
Я не собираюсь знакомить вас с взаимодействием с частными API; вы можете прочитать другие статьи, в которых будет гораздо глубже, чем я когда-либо хотел бы. Без лишних слов, это то, что я придумал.
Шаг 1. Прочтите файлы заголовков с обратной инженерией, которые другие более умные люди разместили на github (https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/). Действительно стоит прочитать все файлы заголовков, потому что некоторые вещи, которые вы можете делать, действительно интересны.
Шаг 2: Приступите к делу. Теперь я буду делать это в Swift, но я предоставлю ссылки на Objective-C ниже. Я бы порекомендовал сделать это в Objective-C, потому что большинство этих классов написано на Objective-C, а многие методы начинаются с "_" быстрых ошибок для iVars, и это становится действительно беспорядочным. Но если нужно, вот как бы вы это сделали.
Таким образом, вместо обычного способа [class performSelector:methodName]
для методов и [instance valueForKey:_iVar]
для доступа к переменным экземпляра гораздо проще использовать протоколы в Swift; в основном потому, что выполняем селектор ОТСАСЫВАЕТ в Swift.
Итак, мы собираемся создать локальное представление классов, которые нам позже понадобятся.
@objc protocol MPAVRoutingControllerProtocol {
optional func fetchAvailableRoutesWithCompletionHandler(completion: (routes: [MPAVRouteProtocol]) -> Void)
optional func pickRoute(route: MPAVRouteProtocol) -> Bool
optional func setDelegate(delegate: NSObject)
}
@objc protocol MPAVRouteProtocol {
optional func routeName() -> String
optional func routeUID() -> String
optional func isPicked() -> Bool
optional func wirelessDisplayRoute() -> MPAVRouteProtocol
}
@objc protocol MPAudioDeviceControllerProtocol {
optional func setRouteDiscoveryEnabled(enabled: Bool)
optional func routeDescriptionAtIndex(index: Int) -> [String: AnyObject]
}
extension NSObject : MPAVRoutingControllerProtocol, MPAVRouteProtocol, MPAudioDeviceControllerProtocol {
}
Вот где действительно происходит волшебство. Где-то в вашем контроллере представления мы создаем классы, используя NSClassFromString()
let MPAudioDeviceControllerClass: NSObject.Type = NSClassFromString("MPAudioDeviceController") as! NSObject.Type
let MPAVRoutingControllerClass: NSObject.Type = NSClassFromString("MPAVRoutingController") as! NSObject.Type
Затем мы создаем объекты из этих классов и взаимодействуем с ними, вызывая функции нашего протокола.
let routingController = MPAVRoutingControllerClass.init() as MPAVRoutingControllerProtocol
let audioDeviceController = MPAudioDeviceControllerClass.init() as MPAudioDeviceControllerProtocol
Теперь, когда у нас есть полностью инициализированные объекты, мы можем делать самые интересные вещи. Итак, первое, что нам нужно сделать, это запустить демон для поиска устройств трансляции.
audioDeviceController.setRouteDiscoveryEnabled!(true)
Этот шаг не является обязательным, но рекомендуется. Вместо того, чтобы обновлять ваш пользовательский интерфейс для изменений, которые могут или не могут быть там, мы можем назначить себе делегата MPAVRoutingController и получить вызов делегата, когда устройства станут активными.
routingController.setDelegate!(self)
Затем мы реализуем функцию.
func routingControllerAvailableRoutesDidChange(controller: MPAVRoutingControllerProtocol) {
}
Затем внутри этой функции мы можем заполнить наш источник данных для нашего пользовательского интерфейса.
routingController.fetchAvailableRoutesWithCompletionHandler! { (routes) in
}
Итак, начнем с того, что этот метод, который мы вызываем, routingController
вернет нам массив объектов MPAVRoute, о которых мы можем извлечь информацию. Вся необходимая информация находится в MPAVRouteProtcol, но если вам понадобится больше, вы можете позвонить audioDeviceController.routeDescriptionAtIndex!(an Index)
. Это вернет подробную информацию, такую как тип устройства трансляции, MAC-адрес, поддерживает ли оно видео и многое другое.
Затем, когда вам нужно выбрать маршрут, вы должны позвонить routingController.pickRoute!(selectedRoute)
. Вам не нужно беспокоиться о паролях, iOS позаботится об этом автоматически (ошибки, кеширование контроллеров предупреждений и т. Д.).
person
Mark Bourke
schedule
04.07.2016