Разработчик мобильных приложений
0,0
рейтинг
23 октября 2015 в 14:36

Разработка → Три наиболее значимых нововведения с приходом watchOS2 из песочницы

Итак, о чем действительно стоит упомянуть при переходе к разработке на watchOS2 — это следующие три пункта:

  • WKInterfacePicker
  • Complications
  • WCSession

В данной статье я коротко расскажу о каждом из них.

WKInterfacePicker


WKInterfacePicker — это нововведение от Apple, которое расширяет возможности разработки в разы. Что же это такое? Если проводить аналоги с разработкой под iOS, то это UIPickerView для часов, однако с более гибким функционалом. Управление реализуется при помощи Digital Crown (боковое колесико).

Обо всех особенностях я упоминать не буду, а отмечу лишь то, что будет важно на начальных этапах разработки. Итак, WKInterfacePicker имеет 2 важных параметра: Style и Focus Style. Меняя стиль (Style), мы меняем способ отображения информации.

Начну сразу с примера. Вопреки хипстерским представлениям, я предоставлю код, написанный на старой доброй классике — Objective-C.

Добавляем в наш InterfaceController аутлет на пикер:
@property (unsafe_unretained, nonatomic) IBOutlet WKInterfacePicker *picker;

Устанавливаем стиль пикера в режим List.



И копипастим код:

- (void)willActivate {
    // This method is called when watch view controller is about to be visible to user
    [super willActivate];
    
    NSArray* arrayOfTexts = @[
                              @"Раз",
                              @"Два",
                              @"Три",
                              @"Четыре",
                              @"Пять",
                              @"Шесть",
                              @"Семь",
                              @"Восемь",
                              @"Девять",
                              @"Десять",
                              @"Одиннадцать",
                              @"Двенадцать",
                              @"Тринадцать",
                              @"Четырнадцать",
                              @"Пятнадцать",
                              @"Шестнадцать",
                              @"Семнадцать",
                              @"Восемнадцать",
                              @"Девятнадцать",
                              @"Двадцать"];
    NSMutableArray* arrayOfItems = [NSMutableArray new];
    
    for (int i = 0; i < arrayOfTexts.count; i++) {
        WKPickerItem *item = [WKPickerItem new];
        [item setTitle: arrayOfTexts[i]];
        [arrayOfItems addObject: item];
    }

    [self.picker setItems:arrayOfItems];
}


В первом случае (List) мы можем отображать текст в привычной нам форме.



Установив пункт Stack, мы получаем набор картинок c предустановленной анимацией.



Выбрав пункт Sequence, мы также можем контролировать установленную картинку, но без анимации, что позволяет использовать этот метод для контроля таких свойств, как громкость, чувствительность, глубина альфа-канала и т.д.



Однако стоит упомянуть кое-что важное: перед началом отрисовки арта следует создать порядка сотни раскадровок с разрешением x2 (x3). Это обеспечит плавную прокрутку и расширит диапазон изменяемых значений.

Кстати говоря, мы можем отслеживать текущий индекс путем создания экшена (способ нестандартный, лично я ожидал метода делегата или что-то вроде метода интерфейс контроллера для таблицы table didSelectRowAtIndex).

Тянем пикер в хедер и прописываем название метода, вроде такого:

- (IBAction)pickerDidSelectValue:(NSInteger)value;

Далее контролируем полученное значение по своему желанию.

Изменяя параметр Focus Style, мы можем установить или отключить видимость границ пикера. Однако на данный момент нет возможности изменить какие-либо свойства фокуса (в активном состоянии пикера фокус зеленого цвета с дефолтными закруглениями).







В целом WKinterfacePicker — это мощный инструмент, который открывает огромные возможности для разработчика.

Complications


По своей сути Complications — это маленькие элементы, которые мы видим при открытии Modular watch face. Дословный перевод слова complications — осложнения. Случайность? Не думаю. Ведь оболочкой для такого, казалось бы, несложного функционала является целый фреймворк ClockKit. При первом просмотре содержимого можно ужаснуться, хотя все не так грустно. Давайте разберем, максимально кратко, так как данной тема стоит отдельной статьи.

При создании нового проекта нам предлагают возможность подключения «осложнений». При поставленной галочке нашему проекту добавляется класс ComplicationController.



К хедеру этого класса подключается протокол CLKComplicationDataSource. В имплементации предустановлено 9 методов. Первые 4 фактически устанавливают условия для поддержки Complications в режиме путешествия во времени (Time Travel). Нас интересует иной метод.

- (void)getCurrentTimelineEntryForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationTimelineEntry * __nullable))handler {
}

Он здесь ключевой. В нем мы устанавливаем «точку входа» нашего complication с указанными параметрами и датой. Выглядит это следующим образом: сначала мы создаем пустой объект класса CLKComplicationTimelineEntry.

CLKComplicationTimelineEntry* entry = nil;

Затем мы пляшем от того, какой тип complication хотим использовать. Для примера возьмем Modular Small и поставим туда картинку.

    if (complication.family == CLKComplicationFamilyModularSmall) {
        CLKComplicationTemplateModularSmallSimpleImage* image = [[CLKComplicationTemplateModularSmallSimpleImage alloc] init];
        [image setImageProvider: [CLKImageProvider imageProviderWithOnePieceImage:[UIImage imageNamed:@"cat"]]]; // не забываем найти png с котейкой :)
        entry = [CLKComplicationTimelineEntry entryWithDate:[NSDate date] complicationTemplate:image];
    }
    handler(entry);

Затем кастомизируем выбранный Modular watch face и получаем забавного котейку от нашего тестового приложения. Profit.







По аналогичным схемам можно устанавливать картинки и тексты разного формата (единично, блоками и колонками). В целом, очень удобно и облегчает жизнь пользователю, которому важно оперативно узнавать последнюю информацию из вашего приложения.

WCSession


Пришла новая эра синхронизации между iPhone и Apple Watch. Если вы застали время дельного «костыля» от Apple под названием openParentApplication или пользовались AppGroup вместе с червоточиной, то пора о них забыть. Теперь есть 3 способа передачи информации между устройствами с использованием «сессии»:

  • Application Context
  • Transferring Files and Data
  • Sending Messages

Первое, что необходимо сделать — это подключить фреймфорк WatchConnectivity. Он-то и обеспечивает возможность коммуникации между устройствами.

  #import <WatchConnectivity/WatchConnectivity.h>

Предварительно подключаем протокол WCSessionDelegate в классах, между которыми будем производить трансфер. Далее проверяем поддержку сессий. Если все хорошо, то включаем. В дальнейшем все обращения к сессии производим через [WCSession defaultSession].

if ([WCSession isSupported]) {
      WCSession *session = [WCSession defaultSession];
      session.delegate = self;
      [session activateSession];
}


NSDictionary *dict = @{@"key": @"То, что мы так хотим передать"};
[[WCSession defaultSession] updateApplicationContext:dict error:nil];

//для получения на обратной стороне используем метод 
-(void)session:(WCSession *)session didReceiveApplicationContext:(NSDictionary<NSString *,id> *)applicationContext {
  NSString* stringWeNeed =  [applicationContext objectForKey:@"key"];
    NSLog(@"%@", stringWeNeed);
}

Если необходима постоянная синхронизация между телефоном и часами, то это наилучший способ. Следующий вариант позволяет передать информацию при помощи 2-х методов (последовательно, начиная с последних изменений перед потерей синхронизации между устройствами):

transferUserInfo: (отправка NSDictionary)
transferFile: metadata: (отправка файла по URL + NSDictionary с метаданными)

По аналогии:

session didReceiveUserInfo: (получаем NSDictionary)
session didReceiveFile: (получаем WCSessionFile с сыллкой на файл и библиотекой с метаданными)

Ну и последний вариант рассмотрим на конкретном примере. Как уже было сказано, импортируем фреймворк WatchConnectivity в стандартных классах ViewController и InterfaceController. Подключаем протокол WatchConnectivity:

@interface ViewController : UIViewController <WCSessionDelegate>
@interface InterfaceController : WKInterfaceController <WCSessionDelegate>

В имплементации прописываем:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    if ([WCSession isSupported]) {
        WCSession *session = [WCSession defaultSession];
        session.delegate = self;
        [session activateSession];
    }
}

- (void)awakeWithContext:(id)context {
    [super awakeWithContext:context];

    if ([WCSession isSupported]) {
        WCSession *session = [WCSession defaultSession];
        session.delegate = self;
        [session activateSession];
    }
}

Будем отображать информацию на часах при клике кнопки на iPhone. Для этого добавляем кнопку в Main.storyboard и тянем IBAction: - (IBAction)buttonClicked:(id)sender;.

В Interface.storyboard добавляем лейбл и перетягиваем Outlet:

@property (unsafe_unretained, nonatomic) IBOutlet WKInterfaceLabel *infoHere;

Нажатием на кнопку мы создаем рандомное число и передаем его на часы, посредством NSDictionary:

- (IBAction)buttonClicked:(id)sender {
   NSString* randomValue = [NSString stringWithFormat:@"%u", (arc4random() % 10000) ]; 
    [[WCSession defaultSession] sendMessage:@{@"key":  randomValue]} replyHandler:^(NSDictionary<NSString *,id> * _Nonnull replyMessage) {
        
    } errorHandler:^(NSError * _Nonnull error) {
        
    }];
    

На обратной стороне прописываем:

-(void)session:(WCSession *)session didReceiveMessage:(NSDictionary<NSString *,id> *)message replyHandler:(void (^)(NSDictionary<NSString *,id> * _Nonnull))replyHandler {

     dispatch_async(dispatch_get_main_queue(), ^{
         
    NSString* string = [message objectForKey:@"key"];
         [self.label setText:string];
     });
}


Здесь есть один важный момент. Мы обновляем UI в главном потоке асинхронно, иначе будут проблемы с отображением. Для понимания причин советую почитать статьи и документацию о многопоточности.

Подытожим. Я перечислил несколько новшеств, которые лично считаю наиболее значимыми с приходом OS2. Несомненно, о каждом из них можно рассказать больше, причем достаточно объемно, но каждому из нас требуются базовые знания для освоения той или иной области. Так пусть эта статья станет той точкой опоры для программистов, решивших связать себя с разработкой под watchOS.
Alex @Academ_Media_Development
карма
4,0
рейтинг 0,0
Разработчик мобильных приложений
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (7)

  • –1
    Спасибо за урок. Расскажите, можно ли передавать с часов на смартфон информацию от датчиков? С примером, пожалуйста.
    • –1
      Здравствуйте) хочу сказать, что эта тема также обширна и также достойна отдельной темы)
      Первое, что необходимо сделать (при наличии аккаунта разработчика) — это подключить HealthKit в Capabilities для Iphone и AppleWatch Extension таргетов.
      Второе — сделать запрос авторизации

      if ([HKHealthStore isHealthDataAvailable]) {
      HKHealthStore *healthStore = [[HKHealthStore alloc] init];
      NSSet *shareTypes = [NSSet setWithObjects:
                                 [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass], // пример
                                 nil];
      
      NSSet *readObjectTypes  = [NSSet setWithObjects:
                                 [HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierDateOfBirth],  // пример
                                 nil];
      
      [healthStore requestAuthorizationToShareTypes:shareObjectTypes
                                          readTypes:readObjectTypes
                                         completion:^(BOOL success, NSError *error) {
                                            
                                             if(success)
                                             {
                                                 // здесь ваши действия при успешной авторизации
                                             }
                                             else
                                             {
                                                 // в случае ошибки авторизации
                                             }
                                             
                                         }];
      }
      
      • 0
        Далее получаем значения в указанном диапазоне времени:
        // установка дат для выборки 
        NSDate *startDate = //...
        NSDate *endDate = //...
        
        HKSampleType *sampleType = [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
        
        // создание выборки по указанным параметрам
        NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];
        
        // создание сортировки по указанным параметрам
        NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate ascending:YES];
        
        // получение потока значений с необходимыми параметрами
        HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:sampleType
                                                                     predicate:predicate
                                                                         limit:HKObjectQueryNoLimit
                                                               sortDescriptors:@[sortDescriptor]
                                                                 resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
                                                                    
                                                                     if(!error && results)
                                                                     {
        
                                                                         for (int i = 0, i < results, i++)
                                                                         {
                                                                             // NSLog ("%@", rusults [i]);
                                                                         }
                                                                     }
                                                                     
                                                                 }];
        
        // здесь выполнение запроса
        [healthStore executeQuery:sampleQuery];
        


        Далее смотрим по обстоятельствам и в удобной форме делаем пересылку данных на IPhone, используя текущую статью)
        • 0
          Я, честно говоря, не нашел примера в статье про передачу данных с часов на смартфон. Что касается данных с акселерометра и прочих часовых датчиков, то доступа к ним нет (пока нет) и передать их на айФон никоим образом нельзя. Буду рад, если кто опровергнет мои исследования.
  • 0
    Complications правильнее перевести как «усложнения» — этот термин в применении к механическим часам означает дополнительные функции помимо показа времени: число месяца, день недели, турбийон и пр.
    • –1
      Спасибо за уместное дополнение)
  • 0
    История развивается по спирали… кто-то ведь ещё помнит JogDial?

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.