Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Записки Фрилансера: Делимся навыком, часть 2

Anna | 2.07.2014 | нет комментариев

Эта статья — вторая часть серии «Записки Фрилансера».

Оглавление:

  1. Часть 1: Работа с Файлами; Образец Singleton; Работа с Аудио; Работа с Видео; In-App Purchases
  2. Часть 2: Личные всплывающие окна (Popups); Как применять Modal Segue в Navigation Controller; Core Graphics; Работа с UIWebView и ScrollView
  3. Часть 3: Жизнь без Autolayout; Splash Screen; Работа с ориентацией девайса в iOS 6 ; Сдвиг содержимого UITextField
  4. Часть 4: Google Analytics; Push Notifications; PSPDFKit; Вход в приложение через Facebook; Рассказать друзьям — Facebook, Twitter, Email
  5. Часть 5: Core Data; UITableView и UICollectionView

Разберемся с кастомизируемыми всплывающими окнами в конце статьи, так как это достаточно обширная тема. А пока что — все остальное.

Как применять Modal Segue в Navigation Controller

И так, перед вами стоит задача: заказчику не нравится типовой переход Navigation Controller’а — «нахлест справа» — и он хочет, Дабы новейший экран, скажем, возникал «поворотом экрана снизу». Сразу видим вариант решения задачи: поменять вид Segue на Modal. Все было бы отлично, да вот только логика Navigation Controller’a с его иерархией экранов нарушается; и как следствие, приложение вылетает. То есть сейчас нам необходима Modal Segue, но с функциями Push Segue. Подозреваю, что есть методы примитивней решить эту задачу, но я предлагаю легко написать подкласс UIStoryboardSegue. Исключительное, что нам предстоит поменять, это .m файл нашего класса. А вернее, способ perform:

Жми меня!

- (void) perform{
    // Получаем экраны, с которыми будем трудиться
    UIViewController *src = (UIViewController *) self.sourceViewController;
    UIViewController *dst = (UIViewController *) self.destinationViewController;

   // Осуществляем примитивный переход
    [UIView transitionFromView:src.view
                        toView:dst.view
                      duration:1
                       options:UIViewAnimationOptionTransitionFlipFromBottom
                    completion:nil];

    // Осуществляем переход для Navigation Controller'a
    [UIView transitionFromView:src.navigationItem.titleView
                        toView:dst.navigationItem.titleView
                      duration:1
                       options:UIViewAnimationOptionTransitionFlipFromBottom
                    completion:nil];

    // Добавляем Push нашей Segue
    [src.navigationController pushViewController:dst animated:NO];
}

Взамен UIViewAnimationOptionTransitionFlipFromBottom дозволено поставить всякий ближний сердцу вашего заказчика жанр перехода.
Вот и все! Как легко, скажете вы. Сейчас мы можем указать для всякий Storyboard Segue жанр Custom, указать наш новейший класс и заполучить Navigation Controller Segue со своим типом перехода.

Core Graphics

В один красивый момент вашему заказчику наскучило пинать дизайнера всякий раз, когда необходимо изменить ширину кнопки на 5 пикселов (чай для всякой кнопочки дизайнер нарисовал отдельную картинку). Сделаем собственную кнопку с закругленными краями и и рамкой вокруг при помощи QuartzCore.framework. Вновь же, как и с синглтоном, это скорее сниппет, ускоряющий работу над планом.

Вообще-то данный подход дозволено применять с любым подклассом UIView (UIButton как-раз им и является). Мы переписываем способ drawRect: у нашего UIView:

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];

    self.layer.cornerRadius = 5.0f;
    self.layer.masksToBounds = YES;

    self.layer.borderColor = [UIColor whiteColor].CGColor;
    self.layer.borderWidth = 1.0f;
}

Все легко. Во-первых, при наследовании нам необходимо исполнить наш код позже того, как суперкласс завершит свою работу. Так что мы вызываем тот же способ, но у суперкласса. Во-вторых, мы задаем радиус закругления углов у слоя нашего вида и принуждаем слой подчиняться указанной маске. В-третьих, мы задаем цвет рамки (CGColor, безусловно) и ее толщину.
Я знаю, что некоторые ждали глубокой работы с графическим контекстом, но на то они и стремительные шпаргалки фрилансера — когда появится новая задача с Core Graphics, тогда и будем писать статьи.

Работа с UIWebView и ScrollView

У нас теснее есть UIWebView, давайте подгрузим в него контент:

NSString *htmlString;
NSString *cssString;

<...Инициализируем строки...>

htmlString = [NSString stringWithFormat:@"<style>%@</style>%@", cssString, htmlString];
NSURL *url = [[NSURL alloc] initFileURLWithPath:pathToApplicationDirectory];

[webView loadHTMLString:htmlString baseURL:url];

Легко получили веб страничку с нашим жанром и html. Но какая неприятность! При попытке прокрутить страницу вниз, если включено «отпрыгивание», то при слишком мощном скроллинге, мы увидим серые (дюже уродливые!) поля оверскролла. Я предлагаю унаследовать подлинный UIWebView и слегка изменить способы инициализации:

Жми меня!

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self hideOverscrollShadowsForWebView:self];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self hideOverscrollShadowsForWebView:self];
    }
    return self;
}

Необходимо переписать оба способа инициализации, потому что в своем коде вы скорее будете применять способ initWithFrame:; в то время, как UIWebView из Stroryboard будет вызывать initWithCoder:.
И, безусловно же, сам способ, прячущий неприятные глазу серые тени:

/*!
 Способ, прячущий немыслимо жуткие и уродливые тени у данного UIWebView
 param webView Собственно сам WebView, тот, что необходимо модифицировать
 */
- (void)hideOverscrollShadowsForWebView:(UIWebView *)webView {
    id scrollview = [webView.subviews objectAtIndex:0];
    for (UIView *subview in [scrollview subviews])
        if ([subview isKindOfClass:[UIImageView class]])
            subview.hidden = YES;

    webView.backgroundColor = [UIColor clearColor];
}

Мы прячем все дочерние виды у Scrollview, принадлежащего нашему UIWebView. А так же устанавливаем прозрачный задний фон.

О UIScrollView дозволено сказать только одно: никогда не забывайте устанавливать качество contentSize, и будет вам блаженство.

Личные всплывающие окна (Popups)

Пришло время побеседовать о гиганте этой статьи. В данный раз заказчику понадобилась вероятность стремительно и без вашего участия менять вид всплывающих окон. Что же, за дело!

Давайте побеседуем немножко о теории, о том, как мы все провернем.
В Storyboard у нас вряд ли получится реализовать полновесное всплывающее окно, а вот сделать обособленный файл PopupView.xib мы можем!
А в качестве модели мы сотворим три класса PopupView, PopupController, PopupControllerDelegate.

Вначале соберите макет своего всплывающего окна как на скриншоте. Учтите, что File’s Owner в нашем случае будет объектом класса PopupController, а сам View будет объектом класса PopupView. Background View — это полупрозрачный ясно-серый UIView.

Посмотрим вначале на реализацию PopupView.h:

Жми меня!

#import <UIKit/UIKit.h>
@interface PopupView : UIView

@property (strong, nonatomic) IBOutlet UIView *backgroundView;
@property (strong, nonatomic) IBOutlet UIView *innerPopupView;
@property (strong, nonatomic) IBOutlet UILabel *popupTitleLabel;
@property (strong, nonatomic) IBOutlet UILabel *popupTextLabel;
@property (strong, nonatomic) IBOutlet UIButton *popupButton;

@end

Мы легко зацепили все элементы пользовательского интерфейса в код; PopupView.m мы не изменяли, оставили типовой сгенерированный код.
У нас есть образец всплывающего окна, сейчас нам необходимо искусно его применять. Создаем PopupController.

PopupController.h:

Жми меня!

#import <Foundation/Foundation.h>
#import "PopupControllerDelegate.h"
#import "PopupView.h"

@interface PopupController : NSObject

// UIViewController, тот, что и будет разбираться с действиями в PopupView
@property (strong, nonatomic) UIViewController<PopupControllerDelegate> *delegate;
// Массив энергичных всплывающих окон. Их может быть несколько сразу!
@property (strong, nonatomic) NSMutableArray *activePopups;

- (IBAction)touchedButton:(UIButton *)sender;

- (id)initWithDelegate:(UIViewController<PopupControllerDelegate> *)delegate;
- (void)showHelloWorldPopup;
- (void)dismissAllPopups;

@end

Все что мы сделали — это установили надобные нам свойства и публичные способы инициализации, показа простенького всплывающего окна, закрытия всех всплывающих окон. А еще добавили маленький Action для кнопочки внутри всплывающего окна в PopupController.

PopupController.m:

Жми меня!

#import "PopupController.h"

@implementation PopupController

- (id)initWithDelegate:(UIViewController<PopupControllerDelegate> *)delegate {
    self = [super init];
    if (self) {
        // Инициализируем массив всплывающих окон
        self.activePopups = [NSMutableArray array];

        // Установим себе делегата
        self.delegate = delegate;
    }
    return self;
}

- (void)showHelloWorldPopup {
    PopupView *popup = [self popupFromRestorationID:@"text"];
    [self configurePopup:popup];
    [self showPopup:popup];
}

- (IBAction)touchedButton:(UIButton *)sender {
    [self.delegate touchedPopupButton:sender];
}

- (void)dismissAllPopups {
    for (UIView *popup in activePopups) {
        [self hidePopup:popup];
    }
}
<...>

Во время инициализации мы сразу устанавливаем делегат нашего класса.
Код способа showHelloWorldPopup я поделил на три других способа для упрощения чтения: мы инициализируем всплывающее окно, настраиваем его и показываем.
Когда пользователь дотрагивается до кнопки на всплывающем окне, мы уведомляем об этом наш делегат.
Способ dismissAllPopups легко проходится по каждому всплывающим окнам в всеобщем массиве и закрывает всякое.
Допищем недостающие способы:

Жми меня!

<...>
- (PopupView *)popupFromRestorationID:(NSString *)restorationID {
    // Заполучаем все виды из нашего .xib файла
    NSArray *allViews = [[NSBundle mainBundle] loadNibNamed:@"PopupView.xib" owner:self options:nil];

    // Пройдемся по каждому видам
    for (PopupView *view in allViews) {
        // Если restorationIdentifier тот, что нам необходим, то возвращаем окно, делаем его прозрачным и добавляем к делегату
        if ([view.restorationIdentifier isEqualToString:restorationID]) {
            view.alpha = 0.0f;
            [self.delegate.view addSubview:view];
            return view;
        }
    }
    // Не обнаружили окно! Вернем пустоту
    return nil;
}

- (void)showPopup:(PopupView *)popup {
    // Уменьшим innerPopupView до 50%
    [popup.innerPopupView setTransform:CGAffineTransformMakeScale(0.5, 0.5)];

    // Начинаем анимацию
    [UIView animateWithDuration:0.2f
                     animations:^{
                         // Возвращаем видимость всплывающего окна
                         popup.alpha = 1.0f; 

                         // Возвращаем размер всплывающему окну до 100%
                         [popup.innerPopupView setTransform:CGAffineTransformMakeScale(1.0, 1.0)];
                     }];

    // Добавим селектор нажатию на задний фон
    [popup.backgroundView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissAllPopups)]];

    // Добавим всплывающее окно в массив всплывающих окон
    [activePopups addObject:popup];
}

- (void)hidePopup:(UIView *)popup {
    // Начинаем анимацию
    [UIView animateWithDuration:0.2f
                     animations:^{
                         // Делаем всплывающее окно прозрачным
                         popup.alpha = 0.0f;
                     }
                     completion:^(BOOL finished){
                         // Удаляем всплывающее окно из нашего ViewController'a
                         [popup removeFromSuperview];
                     }];

    // Удаляем конечный указатель на всплывающее окно из массива, механически выгружаем из памяти
    [activePopups removeObject:popup];
}

- (void)configurePopup:(PopupView *)popup forName:(NSString *)name {
    // Установим заголовок
    popup.popupTitleLabel.text = @"Popup Title";

    // Вставим текст
    popup.popupTextLabel.text = @"Hello World!";

    // Установим текст для кнопки
    [self setTitle:@"Okay"];
}

- (void)setTitle:(NSString *)title forButton:(UIButton *)button {
    [button setTitle:title forState:UIControlStateNormal];
    [button setTitle:title forState:UIControlStateSelected];
    [button setTitle:title forState:UIControlStateHighlighted];
    [button setTitle:title forState:UIControlStateDisabled];
}

@end

Все хитрости поочередно:

  1. Анимации при помощи UIView: все, что написано в блоке animations:, будет понемногу выполняться в течение установленного времени. Код из блока completion: выполнится мигом, сразу позже блокаanimations:
  2. Когда пользователь нажимает на серое затемнение, всплывающее окно сразу пропадает
  3. Если легко установить текст кнопки для одного состояния, то для других текст не изменится, следственно способ setTitle: forButton: меняет текст кнопки сразу для всех состояний

Сделав сходственную архитектуру всплывающих окон, мы даем вероятность заказчику менять внешний вид окон без нашего участия. Пускай заказчик меняет цвет, размер, расположение элементов — пока он не трогает связи с кодом, все будет трудиться, как нужно.

Ну, и на последок, покажу, как работает наш класс. Допишем PopupControllerDelegate.h:

#import <Foundation/Foundation.h>
@protocol PopupControllerDelegate

@required
- (void)touchedPopupButton:(UIButton *)sender;

@end

Всякий UIViewController, на котором может возникнуть всплывающее окно, должен иметь вероятность обрабатывать события этого окна, отвечать на протокол PopupControllerDelegate.

Скажем, мы хотим показать HelloWorld окошко позже загрузки нашего вида, тот, что отвечает надобному протоколу и у которого теснее есть инициализированный объект popupController. Добавим дальнейший код вviewDidAppear:

[popupController showHelloWorldPopup];

И добавим обработчик события всплывающего окна:

- (void)touchedPopupButton:(UIButton *)sender {
    // Легко закроем все всплывающие окна
    [popupController dismissAllPopups];
}

Завершение

Спасибо за то, что дочитали до конца!

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

В следующих статьях будут темы еще увлекательнее: расшаривание контента в общественных сетях, PSPDFKit, Push Notifications.

 

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB