Впровадження NSTouchBar на Swift

Нещодавно Apple представила світу нову лінійку MacBook Pro. І однією з особливостей свіжої версії стало те, що верхній ряд системних кнопок в ній видалений – вірніше, замінений на мультитач екран. Розробникам це нововведення повинно бути цікаво в першу чергу, адже на панелі виділена область, яку можна використовувати у власних додатках. Компанія Apple навіть надала API для її використання. У цій статті ми розповімо і покажемо, як освоювали можливості NSTouchBar. Отримані знання ми надалі застосували в апдейте MaCleaner.




Для початку давайте розберемося, що взагалі являє собою NSTouchBar.



Application Region – це, власне, та сама частина, яка відводиться під потреби програми. Саме тут буде відображатися все, що необхідно для коректної його роботи.

Control Strip – системна панель. Сюди розробнику вхід закритий – ні поміщати свої об'єкти, ні як ще її модифікувати ми не можемо. Тут знаходяться кнопки, які раніше були на місці NSTouchBar – зміна яскравості, гучності та інші.

System Button – системна кнопка

Для того, щоб впроваджувати підтримку NSTouchBar в свої проекти, необов'язково мати новенький MacBookPro з тачбаром. В Xcode є його симулятор, який викликається через Window > Show Touch Bar. Однак скористатися ним ви зможете тільки за умови, що у вашого Xcode версія мінімум 8.1, а у системи – 10.12.1, причому з білдів не нижче 16B2657.

Отже, ви багато працювали і написали чудове додаток – воно висить в статус барі показує у спливаючому вікні системне оформлення робочого столу і змінює його на той прикид, який вибере користувач. Виглядає все це так:



Якщо ви загорілися цим чудовим додатком – можете завантажити версію без NSTouchBar тут і працювати над ним разом з нами.

Тепер введемо в наш проект підтримку NSTouchBar. Для початку підключимо NSTouchBar і виведемо в ньому «Hello World!». Додамо в наш MainPopoverController наступне розширення:

@available(OSX 10.12.1, *)
extension MainPopoverController : NSTouchBarDelegate {

override open func makeTouchBar() -> NSTouchBar? {
let touchBar = NSTouchBar()
touchBar.delegate = self
touchBar.customizationIdentifier = NSTouchBarCustomizationIdentifier("My First TouchBar")
touchBar.defaultItemIdentifiers = [NSTouchBarItemIdentifier("HelloWorld")]
touchBar.customizationAllowedItemIdentifiers = [NSTouchBarItemIdentifier("HelloWorld")]
return touchBar
}

}


У методі makeTouchBar() ми створюємо об'єкт NSTouchBar і вказуємо його делегат. Кожен NSTouchBar і кожен NSTouchBarItem повинні мати унікальні ідентифікатори. Тому ми прописуємо ідентифікатор для нашого NSTouchBar, потім вказуємо ідентифікатори об'єктів, які в нього помістимо, і, нарешті, задаємо порядок відображення об'єктів. Тепер додамо метод, який буде заповнювати наш NSTouchBar об'єктами:

func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
switch identifier {
case NSTouchBarItemIdentifier("HelloWorld"):
let customViewItem = NSCustomTouchBarItem(identifier: identifier)
customViewItem.view = NSTextField(labelWithString: "Hello World!")
return customViewItem
default:
return nil
}
}


У цьому методі заповнювати NSTouchBar можна як завгодно. Для даного прикладу ми створили NSCustomTouchBarItem і поставили в нього NSTextField з текстом «Hello World!». Тепер настав час запустити проект і насолоджуватися нашим ексклюзивним NSTouchBar! Запускаємо – і…




Де ж наш «Hello World!»? А справа ось в чому. Для того, щоб ми могли згенерувати NSTouchBar в MainPopoverController, хоча б один об'єкт в поповере повинен отримати фокус. У нашому поповере є два об'єкти – сам NSView і CollectionView. У NSView (і CollectionView) acceptsFirstResponder за замовчуванням завжди повертає false – таким чином, коли ми запускаємо додаток, фокус не вміщується ні на один об'єкт, а отже, і NSTouchBar не генерується. Ми вийдемо з положення наступним чином: створимо свій NSView і переопределим acceptsFirstResponder так, щоб він повертав true.

class MainPopoverView: NSView {

override var acceptsFirstResponder: Bool {
get {
return true
}
}

}


Спробуємо запустити програму після використання цього класу в нашому поповере і подивимося, що вийшло:




Наш NSTouchBar нарешті явив себе світу і навіть з цим світом привітався. Тепер нам треба реалізувати NSTouchBar застосовно до функціоналу нашого застосування. Ми хочемо помістити в NSTouchBar всі картинки з нашої колекції з тим, щоб при натисканні на картинку додаток ставило її фоном для робочого столу. Так як для всієї галереї в NSTouchBar місця не вистачить, будемо застосовувати прокручується NSScrubber. Для початку змінимо метод, що заповнює наш NSTouchBar об'єктами, щоб він використовував NSScrubber.

func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
switch identifier {
case NSTouchBarItemIdentifier("HelloWorld"):
let scrubberItem = NSCustomTouchBarItem(identifier: identifier)
let смуги навігації = NSScrubber()
смуги навігації.scrubberLayout = NSScrubberFlowLayout()
смуги навігації.register(NSScrubberImageItemView.self, forItemIdentifier: "ScrubberItemIdentifier")
смуги навігації.mode = .free
смуги навігації.selectionBackgroundStyle = .roundedBackground
смуги навігації.delegate = self
смуги навігації.dataSource = self
scrubberItem.view = смуги навігації
return scrubberItem
default:
return nil
}
}


Для того щоб заповнювати NSScrubber об'єктами, необхідно підписатися на NSScrubberDataSource; для обробки натискання на певний об'єкт з нього – на NSScrubberDelegate, а для того, щоб вказати розмір об'єкта (а в NSTouchBar максимальна висота 30 об'єкта, інакше об'єкт буде обрізатися) – на NSScrubberFlowLayoutDelegate.

У підсумку додамо наступне розширення для нашого MainPopoverController:

@available(OSX 10.12.1, *)
extension MainPopoverController: NSScrubberDataSource, NSScrubberDelegate, NSScrubberFlowLayoutDelegate {
func numberOfItems(for смуги навігації: NSScrubber) -> Int {
return wardrobe.wallpapers.count
}

func смуги навігації(_ смуги навігації: NSScrubber, viewForItemAt index: Int) -> NSScrubberItemView {
let itemView = смуги навігації.makeItem(withIdentifier: "ScrubberItemIdentifier", owner: nil) as! NSScrubberImageItemView
itemView.image = wardrobe.wallpapers[index].picture
return itemView
}

func смуги навігації(_ смуги навігації: NSScrubber, didSelectItemAt index: Int) {
смуги навігації.selectedIndex = -1
if let screen = NSScreen.main() {
do {
try NSWorkspace.shared().setDesktopImageURL( wardrobe.wallpapers[index].url for: screen, options: [:])
} catch {
print(error)
}
}
}

func смуги навігації(_ смуги навігації: NSScrubber, layout: NSScrubberFlowLayout, sizeForItemAt itemIndex: Int) -> NSSize {
return NSSize(width: 60, висота: 30)
}
}


Перший метод повертає кількість об'єктів в NSScrubber. Другий – генерує об'єкт для певної позиції в NSScrubber (в нашому випадку це картинки для робочого столу). Третій – обробляє натискання на об'єкт в NSScrubber і ставить обрану картинку на робочий стіл. Ну а четвертий метод повертає розміри об'єкта в NSScrubber.

Тепер при запуску наш NSTouchBar має наступний вигляд:




Тепер у нас в додатку є робочий NSTouchBar, який може міняти оформлення робочого столу. За таким же алгоритмом можна реалізувати підтримку тачбара і в більш складних проектах, чим ми і плануємо зайнятися в майбутньому. (посилання на фінальну версію проекту з реалізованим NSTouchBar)
Спасибі за увагу і удачі в роботі з новою дивиною від Apple!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.