[Из песочницы] iOS Инструменты разработчика

Вступление


Всем привет, меня зовут Григорий, последние 5 лет занимался программированием под iOS. Сейчас решил сменить сферу деятельности и ударился в веб, но чтобы добро не пропадало, хочу поделиться с сообществом своими наработками, накопившимися за это время. Библиотеки выложены на GitHub и добавлены в CocoaPods. Инструкции по установке и использованию вы сможете найти по ссылкам на GitHub, здесь же будет краткое описание.

Минимальная поддерживаемая версия — iOS 6.0.


Скриншоты


UIAlertView — один из часто используемых компонентов при разработке на iOS. Выглядит и работает из коробки замечательно, проблема в том, что возможностей кастомизировать его Apple практически не дает, есть только несколько стандартно заданных стилей отображения. А что если нам нужно вставить внутрь свою вьюху, или банально поменять цвет кнопок или фона? Вот и было принято решение написать универсальный класс, который повторял бы работу UIAlertView, но имел широкие возможности по настройке. Так и появился LGAlertView.

При инициализации у нас есть возможность выбора между несколькими стилями:

Немного кода
Стандартный (заголовок + сообщение + кнопки):
- (instancetype)initWithTitle:(NSString *)title
                      message:(NSString *)message
                 buttonTitles:(NSArray *)buttonTitles
            cancelButtonTitle:(NSString *)cancelButtonTitle
       destructiveButtonTitle:(NSString *)destructiveButtonTitle;


Cо встроенной вьюхой (заголовок + сообщение + UIView + кнопки):
- (instancetype)initWithViewStyleWithTitle:(NSString *)title
                                   message:(NSString *)message
                                      view:(UIView *)view
                              buttonTitles:(NSArray *)buttonTitles
                         cancelButtonTitle:(NSString *)cancelButtonTitle
                    destructiveButtonTitle:(NSString *)destructiveButtonTitle;


C индикатором активности (заголовок + сообщение + UIActivityIndicatorView + кнопки):
- (instancetype)initWithActivityIndicatorStyleWithTitle:(NSString *)title
                                                message:(NSString *)message
                                           buttonTitles:(NSArray *)buttonTitles
                                      cancelButtonTitle:(NSString *)cancelButtonTitle
                                 destructiveButtonTitle:(NSString *)destructiveButtonTitle;


C полосой прогресса (заголовок + сообщение + UIProgressView + кнопки):
- (instancetype)initWithActivityIndicatorStyleWithTitle:(NSString *)title
                                                message:(NSString *)message
                                           buttonTitles:(NSArray *)buttonTitles
                                      cancelButtonTitle:(NSString *)cancelButtonTitle
                                 destructiveButtonTitle:(NSString *)destructiveButtonTitle;


C полями ввода (заголовок + сообщение + поля ввода + кнопки):
- (instancetype)initWithActivityIndicatorStyleWithTitle:(NSString *)title
                                                message:(NSString *)message
                                           buttonTitles:(NSArray *)buttonTitles
                                      cancelButtonTitle:(NSString *)cancelButtonTitle
                                 destructiveButtonTitle:(NSString *)destructiveButtonTitle;



Для возможности отлавливать события есть несколько путей: делегирование, как в стандартном UIAlertView, и блоки (кому как больше нравится). Кроме того, предусмотрены NSNotification для появления и исчезновения LGAlertView с экрана. Сначала думал добавить нотификации и для кнопок, но решил, что все-таки это будет лишним.

Немного кода
Делегирование:
@property (assign, nonatomic) id delegate;

- (void)alertViewWillShow:(LGAlertView *)alertView;
- (void)alertViewWillDismiss:(LGAlertView *)alertView;
- (void)alertViewDidShow:(LGAlertView *)alertView;
- (void)alertViewDidDismiss:(LGAlertView *)alertView;
- (void)alertView:(LGAlertView *)alertView buttonPressedWithTitle:(NSString *)title index:(NSUInteger)index;
- (void)alertViewCancelled:(LGAlertView *)alertView;
- (void)alertViewDestructiveButtonPressed:(LGAlertView *)alertView;


Блоки:
@property (strong, nonatomic) void (^willShowHandler)(LGAlertView *alertView);
@property (strong, nonatomic) void (^willDismissHandler)(LGAlertView *alertView);
@property (strong, nonatomic) void (^didShowHandler)(LGAlertView *alertView);
@property (strong, nonatomic) void (^didDismissHandler)(LGAlertView *alertView);
@property (strong, nonatomic) void (^actionHandler)(LGAlertView *alertView, NSString *title, NSUInteger index);
@property (strong, nonatomic) void (^cancelHandler)(LGAlertView *alertView, BOOL onButton);
@property (strong, nonatomic) void (^destructiveHandler)(LGAlertView *alertView);


NSNotifications:
kLGAlertViewWillShowNotification;
kLGAlertViewWillDismissNotification;
kLGAlertViewDidShowNotification;
kLGAlertViewDidDismissNotification;



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

Немного кода
/** Default is YES */
@property (assign, nonatomic, getter=isCancelOnTouch) BOOL cancelOnTouch;
/** Set highlighted buttons background color to blue, and set highlighted destructive button background color to red. Default is YES */
@property (assign, nonatomic, getter=isColorful) BOOL colorful;

@property (strong, nonatomic) UIColor *tintColor;
@property (strong, nonatomic) UIColor *coverColor;
@property (strong, nonatomic) UIColor *backgroundColor;
@property (assign, nonatomic) CGFloat layerCornerRadius;
@property (strong, nonatomic) UIColor *layerBorderColor;
@property (assign, nonatomic) CGFloat layerBorderWidth;
@property (strong, nonatomic) UIColor *layerShadowColor;
@property (assign, nonatomic) CGFloat layerShadowRadius;

@property (assign, nonatomic) CGFloat heightMax;
@property (assign, nonatomic) CGFloat widthMax;

@property (strong, nonatomic) UIColor         *titleTextColor;
@property (assign, nonatomic) NSTextAlignment titleTextAlignment;
@property (strong, nonatomic) UIFont          *titleFont;

@property (strong, nonatomic) UIColor         *messageTextColor;
@property (assign, nonatomic) NSTextAlignment messageTextAlignment;
@property (strong, nonatomic) UIFont          *messageFont;

@property (strong, nonatomic) UIColor         *buttonsTitleColor;
@property (strong, nonatomic) UIColor         *buttonsTitleColorHighlighted;
@property (assign, nonatomic) NSTextAlignment buttonsTextAlignment;
@property (strong, nonatomic) UIFont          *buttonsFont;
@property (strong, nonatomic) UIColor         *buttonsBackgroundColorHighlighted;
@property (assign, nonatomic) NSUInteger      buttonsNumberOfLines;
@property (assign, nonatomic) NSLineBreakMode buttonsLineBreakMode;
@property (assign, nonatomic) BOOL            buttonsAdjustsFontSizeToFitWidth;
@property (assign, nonatomic) CGFloat         buttonsMinimumScaleFactor;

@property (strong, nonatomic) UIColor         *cancelButtonTitleColor;
@property (strong, nonatomic) UIColor         *cancelButtonTitleColorHighlighted;
@property (assign, nonatomic) NSTextAlignment cancelButtonTextAlignment;
@property (strong, nonatomic) UIFont          *cancelButtonFont;
@property (strong, nonatomic) UIColor         *cancelButtonBackgroundColorHighlighted;
@property (assign, nonatomic) NSUInteger      cancelButtonNumberOfLines;
@property (assign, nonatomic) NSLineBreakMode cancelButtonLineBreakMode;
@property (assign, nonatomic) BOOL            cancelButtonAdjustsFontSizeToFitWidth;
@property (assign, nonatomic) CGFloat         cancelButtonMinimumScaleFactor;

@property (strong, nonatomic) UIColor         *destructiveButtonTitleColor;
@property (strong, nonatomic) UIColor         *destructiveButtonTitleColorHighlighted;
@property (assign, nonatomic) NSTextAlignment destructiveButtonTextAlignment;
@property (strong, nonatomic) UIFont          *destructiveButtonFont;
@property (strong, nonatomic) UIColor         *destructiveButtonBackgroundColorHighlighted;
@property (assign, nonatomic) NSUInteger      destructiveButtonNumberOfLines;
@property (assign, nonatomic) NSLineBreakMode destructiveButtonLineBreakMode;
@property (assign, nonatomic) BOOL            destructiveButtonAdjustsFontSizeToFitWidth;
@property (assign, nonatomic) CGFloat         destructiveButtonMinimumScaleFactor;

@property (assign, nonatomic) UIActivityIndicatorViewStyle activityIndicatorViewStyle;
@property (strong, nonatomic) UIColor                      *activityIndicatorViewColor;

@property (strong, nonatomic) UIColor *progressViewProgressTintColor;
@property (strong, nonatomic) UIColor *progressViewTrackTintColor;
@property (strong, nonatomic) UIImage *progressViewProgressImage;
@property (strong, nonatomic) UIImage *progressViewTrackImage;

@property (strong, nonatomic) UIColor         *progressLabelTextColor;
@property (assign, nonatomic) NSTextAlignment progressLabelTextAlignment;
@property (strong, nonatomic) UIFont          *progressLabelFont;

@property (strong, nonatomic) UIColor *separatorsColor;

@property (assign, nonatomic) UIScrollViewIndicatorStyle indicatorStyle;



Чтобы показывать или скрывать LGAlertView предусмотрены следующие методы:

- (void)showAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;
- (void)dismissAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;


Кроме того удалось добиться правильного поведения в случае, когда появляется несколько вьюх подряд без закрытия предыдущих. Вы можете не боясь комбинировать UIAlertView, UIActionSheet, LGAlertView и LGActionSheet. При появлении новых старые будут исчезать, а при исчезновении — появляться.

Скриншоты


Причины появления и принцип действия аналогичны LGAlertView.

Стили при инициализации:

Немного кода
Стандартный (сообщение + кнопки):
- (instancetype)initWithTitle:(NSString *)title
                 buttonTitles:(NSArray *)buttonTitles
            cancelButtonTitle:(NSString *)cancelButtonTitle
       destructiveButtonTitle:(NSString *)destructiveButtonTitle;


Cо встроенной вьюхой (сообщение + UIView + кнопки):
- (instancetype)initWithTitle:(NSString *)title
                         view:(UIView *)view
                 buttonTitles:(NSArray *)buttonTitles
            cancelButtonTitle:(NSString *)cancelButtonTitle
       destructiveButtonTitle:(NSString *)destructiveButtonTitle;



События отлавливаются с помощью делегирования, блоков и нотификаций:

Немного кода
Делегирование:
@property (assign, nonatomic) id delegate;

- (void)actionSheetWillShow:(LGActionSheet *)actionSheet;
- (void)actionSheetWillDismiss:(LGActionSheet *)actionSheet;
- (void)actionSheetDidShow:(LGActionSheet *)actionSheet;
- (void)actionSheetDidDismiss:(LGActionSheet *)actionSheet;
- (void)actionSheet:(LGActionSheet *)actionSheet buttonPressedWithTitle:(NSString *)title index:(NSUInteger)index;
- (void)actionSheetCancelled:(LGActionSheet *)actionSheet;
- (void)actionSheetDestructiveButtonPressed:(LGActionSheet *)actionSheet;


Блоки:
@property (strong, nonatomic) void (^willShowHandler)(LGActionSheet *actionSheet);
@property (strong, nonatomic) void (^willDismissHandler)(LGActionSheet *actionSheet);
@property (strong, nonatomic) void (^didShowHandler)(LGActionSheet *actionSheet);
@property (strong, nonatomic) void (^didDismissHandler)(LGActionSheet *actionSheet);
@property (strong, nonatomic) void (^actionHandler)(LGActionSheet *actionSheet, NSString *title, NSUInteger index);
@property (strong, nonatomic) void (^cancelHandler)(LGActionSheet *actionSheet, BOOL onButton);
@property (strong, nonatomic) void (^destructiveHandler)(LGActionSheet *actionSheet);


NSNotifications:
kLGActionSheetWillShowNotification;
kLGActionSheetWillDismissNotification;
kLGActionSheetDidShowNotification;
kLGActionSheetDidDismissNotification;



Настройка внешнего вида и анимаций (по умолчанию анимации для iPhone и iPad различается):

Немного кода
@property (assign, nonatomic) LGActionSheetTransitionStyle transitionStyle;

/** Default is YES */
@property (assign, nonatomic, getter=isCancelOnTouch) BOOL cancelOnTouch;
/** Set highlighted buttons background color to blue, and set highlighted destructive button background color to red. Default is YES */
@property (assign, nonatomic, getter=isColorful) BOOL colorful;

@property (strong, nonatomic) UIColor *tintColor;
@property (strong, nonatomic) UIColor *coverColor;
@property (strong, nonatomic) UIColor *backgroundColor;
@property (assign, nonatomic) CGFloat layerCornerRadius;
@property (strong, nonatomic) UIColor *layerBorderColor;
@property (assign, nonatomic) CGFloat layerBorderWidth;
@property (strong, nonatomic) UIColor *layerShadowColor;
@property (assign, nonatomic) CGFloat layerShadowRadius;

@property (assign, nonatomic) CGFloat heightMax;
@property (assign, nonatomic) CGFloat widthMax;

@property (strong, nonatomic) UIColor         *titleTextColor;
@property (assign, nonatomic) NSTextAlignment titleTextAlignment;
@property (strong, nonatomic) UIFont          *titleFont;

@property (strong, nonatomic) UIColor         *buttonsTitleColor;
@property (strong, nonatomic) UIColor         *buttonsTitleColorHighlighted;
@property (assign, nonatomic) NSTextAlignment buttonsTextAlignment;
@property (strong, nonatomic) UIFont          *buttonsFont;
@property (strong, nonatomic) UIColor         *buttonsBackgroundColorHighlighted;
@property (assign, nonatomic) NSUInteger      buttonsNumberOfLines;
@property (assign, nonatomic) NSLineBreakMode buttonsLineBreakMode;
@property (assign, nonatomic) BOOL            buttonsAdjustsFontSizeToFitWidth;
@property (assign, nonatomic) CGFloat         buttonsMinimumScaleFactor;

@property (strong, nonatomic) UIColor         *cancelButtonTitleColor;
@property (strong, nonatomic) UIColor         *cancelButtonTitleColorHighlighted;
@property (assign, nonatomic) NSTextAlignment cancelButtonTextAlignment;
@property (strong, nonatomic) UIFont          *cancelButtonFont;
@property (strong, nonatomic) UIColor         *cancelButtonBackgroundColorHighlighted;
@property (assign, nonatomic) NSUInteger      cancelButtonNumberOfLines;
@property (assign, nonatomic) NSLineBreakMode cancelButtonLineBreakMode;
@property (assign, nonatomic) BOOL            cancelButtonAdjustsFontSizeToFitWidth;
@property (assign, nonatomic) CGFloat         cancelButtonMinimumScaleFactor;

@property (strong, nonatomic) UIColor         *destructiveButtonTitleColor;
@property (strong, nonatomic) UIColor         *destructiveButtonTitleColorHighlighted;
@property (assign, nonatomic) NSTextAlignment destructiveButtonTextAlignment;
@property (strong, nonatomic) UIFont          *destructiveButtonFont;
@property (strong, nonatomic) UIColor         *destructiveButtonBackgroundColorHighlighted;
@property (assign, nonatomic) NSUInteger      destructiveButtonNumberOfLines;
@property (assign, nonatomic) NSLineBreakMode destructiveButtonLineBreakMode;
@property (assign, nonatomic) BOOL            destructiveButtonAdjustsFontSizeToFitWidth;
@property (assign, nonatomic) CGFloat         destructiveButtonMinimumScaleFactor;

@property (strong, nonatomic) UIColor *separatorsColor;

@property (assign, nonatomic) UIScrollViewIndicatorStyle indicatorStyle;



Чтобы показывать или скрывать LGActionSheet предусмотрены следующие методы:

- (void)showAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;
- (void)dismissAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;


Скриншоты


Без выезжающих боковых меню в наше время не обходится практически ни одно серьезное приложение. В какой-то момент надоело писать для каждого проекта разовые решения и была создана данная библиотека.

Краткий список возможностей:

  • Поддерживается как левое меню, так и правое
  • Различные виды анимаций
  • Показ и скрытие по нажатию кнопки и по жесту
  • Настройка правил показа для разных девайсов и ориентаций (например можно сделать меню нескрываемым для landscape ориентации на iPad)
  • На выбор скрывать или показывать статус бар
  • Широкие возможности по настройке внешнего вида


Добавить данный контроллер в проект довольно просто. Нужно указать ваш корневой контроллер (обычно это UINavigationController) как корневой контроллер LGSideMenuController’a… звучит немного тавтологично, приведу пример:

Обычная инициализация корневого контроллера в AppDelegate.m:

ViewController *viewController = [ViewController new];

UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];

window.rootViewController = navigationController;


Инициализация с LGSideMenuController’ом:

ViewController *viewController = [ViewController new];

UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];

LGSideMenuController *sideMenuController = [[LGSideMenuController alloc] initWithRootViewController:navigationController];

window.rootViewController = sideMenuController;


Настроить боковые меню тоже не сложно. Сначала необходимо включить те меню, которые вам нужны (левое, правое или обе):

[sideMenuController setLeftViewEnabledWithWidth:250.f // необходимая ширина области
                              presentationStyle:LGSideMenuPresentationStyleScaleFromBig // стиль анимации
                           alwaysVisibleOptions:0]; // правила показа


Далее добавьте свои вьюхи, которые будут показываться в боковом меню:

TableViewController *leftViewController = [TableViewController new];

[sideMenuController.leftView addSubview:leftViewController.tableView];


Чтобы показывать или скрывать боковые меню предусмотрены следующие методы:

- (void)showLeftViewAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;
- (void)hideLeftViewAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;
- (void)showHideLeftViewAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;

- (void)showRightViewAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;
- (void)hideRightViewAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;
- (void)showHideRightViewAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;


Также присутствуют следующие NSNotifications:

kLGSideMenuControllerWillShowLeftViewNotification;
kLGSideMenuControllerWillDismissLeftViewNotification;
kLGSideMenuControllerDidShowLeftViewNotification;
kLGSideMenuControllerDidDismissLeftViewNotification;

kLGSideMenuControllerWillShowRightViewNotification;
kLGSideMenuControllerWillDismissRightViewNotification;
kLGSideMenuControllerDidShowRightViewNotification;
kLGSideMenuControllerDidDismissRightViewNotification;


Скриншоты


Google последнее время активно продвигает свой Material Design, одним из компонентов которого является кнопка »+», вызывающая какие-либо дополнительные опции. Решение довольно интересное. На моей практике был заказчик, который просил подобный функционал реализовать на iOS. Поэтому тянуть резину не стал и сразу решил написать универсальное решение.

Краткий список возможностей:

  • Можно добавить на любую вьюху
  • Если добавлено на UIScrollView то при скролле будет скрываться
  • Различные анимации появления кнопок
  • Можно выводить в любом из углов
  • Широкие возможности по настройке внешнего вида


Инициализация:

- (instancetype)initWithView:(UIView *)view
             numberOfButtons:(NSUInteger)numberOfButtons
             showsPlusButton:(BOOL)showsPlusButton;


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

Немного кода
Делегирование:
@property (assign, nonatomic) id delegate;

- (void)plusButtonsView:(LGPlusButtonsView *)plusButtonsView buttonPressedWithTitle:(NSString *)title description:(NSString *)description index:(NSUInteger)index;
- (void)plusButtonsViewPlusButtonPressed:(LGPlusButtonsView *)plusButtonsView;


Блоки:
@property (strong, nonatomic) void (^actionHandler)(LGPlusButtonsView *plusButtonView, NSString *title, NSString *description, NSUInteger index);
@property (strong, nonatomic) void (^plusButtonActionHandler)(LGPlusButtonsView *plusButtonView);



Настройка внешнего вида и анимаций:

Немного кода
@property (assign, nonatomic, getter=isShowWhenScrolling) BOOL showWhenScrolling;

@property (strong, nonatomic) LGPlusButton *plusButton;

/** First is plusButton */
@property (strong, nonatomic) NSMutableArray *buttons;
/** First is plusButton description */
@property (strong, nonatomic) NSMutableArray *descriptions;

@property (assign, nonatomic) UIEdgeInsets contentInset;
@property (assign, nonatomic) UIEdgeInsets buttonInset;
@property (assign, nonatomic) CGSize       buttonsSize;
@property (assign, nonatomic) CGSize       plusButtonSize;
/** Description horizontal offset from button, default is 6.f */
@property (assign, nonatomic) CGFloat      descriptionOffsetX;

@property (assign, nonatomic) LGPlusButtonsAppearingAnimationType appearingAnimationType;
@property (assign, nonatomic) LGPlusButtonsAppearingAnimationType buttonsAppearingAnimationType;
@property (assign, nonatomic) LGPlusButtonAnimationType           plusButtonAnimationType;
@property (assign, nonatomic) LGPlusButtonsViewPosition           position;

- (void)setButtonsTitles:(NSArray *)titles forState:(UIControlState)state;
- (void)setButtonsTitleColor:(UIColor *)titleColor forState:(UIControlState)state;
- (void)setButtonsImage:(UIImage *)image forState:(UIControlState)state;
- (void)setButtonsBackgroundImage:(UIImage *)backgroundImage forState:(UIControlState)state;
- (void)setButtonsBackgroundColor:(UIColor *)backgroundColor forState:(UIControlState)state;
- (void)setButtonsTitleFont:(UIFont *)font;

- (void)setDescriptionsTexts:(NSArray *)texts;
- (void)setDescriptionsTextColor:(UIColor *)textColor;
- (void)setDescriptionsBackgroundColor:(UIColor *)backgroundColor;
- (void)setDescriptionsFont:(UIFont *)font;

- (void)setButtonsClipsToBounds:(BOOL)clipsToBounds;
- (void)setButtonsContentEdgeInsets:(UIEdgeInsets)contentEdgeInsets;
- (void)setButtonsAdjustsImageWhenHighlighted:(BOOL)adjustsImageWhenHighlighted;

- (void)setButtonsLayerMasksToBounds:(BOOL)masksToBounds;
- (void)setButtonsLayerCornerRadius:(CGFloat)cornerRadius;
- (void)setButtonsLayerBorderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth;
- (void)setButtonsLayerShadowColor:(UIColor *)shadowColor shadowOpacity:(float)shadowOpacity shadowOffset:(CGSize)shadowOffset shadowRadius:(CGFloat)shadowRadius;



Чтобы показывать или скрывать LGPlusButtonsView, предусмотрены следующие методы:

// для всех кнопок, включая кнопку "+"
- (void)showAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;
- (void)hideAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;

// только дополнительные кнопки
- (void)showButtonsAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;
- (void)hideButtonsAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;


Скриншоты


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

Краткий список возможностей:

  • Может показывать по умолчанию как таблицу, так и кастомную вьюху
  • Есть несколько стилей на выбор
  • Довольно широкие возможности по настройке внешнего вида


Инициализировать можно либо со списком возможных названий (тогда фильтр будет выглядеть как таблица), либо со своей кастомной вьюхой (которую фильтр будет показывать):

- (instancetype)initWithView:(UIView *)view;
- (instancetype)initWithTitles:(NSArray *)titles;


События отлавливаются с помощью делегирования, блоков и нотификаций:

Немного кода
Делегирование:
@property (assign, nonatomic) id delegate;

- (void)filterViewWillShow:(LGFilterView *)filterView;
- (void)filterViewWillDismiss:(LGFilterView *)filterView;
- (void)filterViewDidShow:(LGFilterView *)filterView;
- (void)filterViewDidDismiss:(LGFilterView *)filterView;
- (void)filterView:(LGFilterView *)filterView buttonPressedWithTitle:(NSString *)title index:(NSUInteger)index;
- (void)filterViewCancelled:(LGFilterView *)filterView;


Блоки:
@property (strong, nonatomic) void (^willShowHandler)(LGFilterView *filterView);
@property (strong, nonatomic) void (^willDismissHandler)(LGFilterView *filterView);
@property (strong, nonatomic) void (^didShowHandler)(LGFilterView *filterView);
@property (strong, nonatomic) void (^didDismissHandler)(LGFilterView *filterView);
@property (strong, nonatomic) void (^actionHandler)(LGFilterView *filterView, NSString *title, NSUInteger index);
@property (strong, nonatomic) void (^cancelHandler)(LGFilterView *filterView);


NSNotifications:
kLGFilterViewWillShowNotification;
kLGFilterViewWillDismissNotification;
kLGFilterViewDidShowNotification;
kLGFilterViewDidDismissNotification;



Настройка внешнего вида и анимаций (по умолчанию анимации для iPhone и iPad различается):

Немного кода
@property (assign, nonatomic) LGFilterViewTransitionStyle transitionStyle;

@property (assign, nonatomic) CGPoint      offset;
@property (assign, nonatomic) UIEdgeInsets contentInset;
@property (assign, nonatomic) CGFloat      heightMax;

@property (assign, nonatomic, getter=isSeparatorsVisible) BOOL separatorsVisible;
@property (strong, nonatomic) UIColor      *separatorsColor;
@property (assign, nonatomic) UIEdgeInsets separatorsEdgeInsets;

@property (strong, nonatomic) UIColor *titleColor;
@property (strong, nonatomic) UIColor *titleColorHighlighted;
@property (strong, nonatomic) UIColor *titleColorSelected;

@property (strong, nonatomic) UIColor *backgroundColorHighlighted;
@property (strong, nonatomic) UIColor *backgroundColorSelected;

@property (strong, nonatomic) UIFont          *font;
@property (assign, nonatomic) NSUInteger      numberOfLines;
@property (assign, nonatomic) NSLineBreakMode lineBreakMode;
@property (assign, nonatomic) NSTextAlignment textAlignment;
@property (assign, nonatomic) BOOL            adjustsFontSizeToFitWidth;
@property (assign, nonatomic) CGFloat         minimumScaleFactor;

@property (assign, nonatomic) CGFloat cornerRadius;
@property (assign, nonatomic) CGFloat borderWidth;
@property (strong, nonatomic) UIColor *borderColor;

@property (assign, nonatomic) UIScrollViewIndicatorStyle indicatorStyle;



Чтобы показывать или скрывать фильтр предусмотрены следующие методы:

- (void)showInView:(UIView *)view animated:(BOOL)animated completionHandler:(void(^)())completionHandler;
- (void)dismissAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;


Скриншоты
Preview.gif1.png


«Потяни, чтобы обновить» — очень модная фича, которая есть практически в каждом приложении. Даже Apple не удержалась и в iOS 6 добавила данный функционал, но почему-то только для UITableView, а UICollectionView и UIScrollView остались за бортом. Хотя при помощи некоторых костылей стандартный «pull to refresh» можно прикрутить и для UICollectionView, но костыли нам не нужны. По правде сказать различных «рефрешей» полно на гитхабе, сам долго искал подходящий, но в основном там либо заброшенные популярные старые версии, у которых накопился ворох различных проблем, или очень крутые библиотеки, которые делают просто невообразимые вещи, но слишком изощрены в дизайне, чтобы была возможность использовать их в любом проекте. Поэтому решил постараться сделать универсальный, кастомизируемый и нейтральный «pull to refresh».

При инициализации нужно указать родительскую вьюху, которая должна быть UIScrollView или наследуемым классом (UITableView или UICollectionView. По идее должно работать и с UIWebView, но во время тестов были проблемы, поэтому не советую).

- (instancetype)initWithScrollView:(UIScrollView *)scrollView;


Событие рефреша отлавливается с помощью делегирования, блоков и нотификаций:

Немного кода
Делегирование:
@property (assign, nonatomic) id delegate;

- (void)refreshViewRefreshing:(LGRefreshView *)refreshView;


Блоки:
@property (strong, nonatomic) void (^refreshHandler)(LGRefreshView *refreshView);


NSNotifications:
kLGRefreshViewBeginRefreshingNotification;
kLGRefreshViewEndRefreshingNotification;



Для завершения обновления предусмотрен метод:

- (void)endRefreshing;


Также рефреш можно вызвать программно:

- (void)triggerAnimated:(BOOL)animated;


Скриншоты


Если у вас клиент-серверное приложение, то при переходе на новый контроллер часто приходится загружать данные. Чтобы не фризить UI, делать это нужно в дополнительном потоке. Но что показывать пользователю, пока происходит загрузка? А показывать можно разное, для этого и сделан LGPlaceholderView.

Что LGPlaceholderView может показывать:

  • Текст
  • UIActivityIndicatorView
  • Текст + UIActivityIndicatorView
  • UIProgressView
  • Текст + UIProgressView
  • Кастомную вьюху


Кроме того, LGPlaceholderView всегда будет находиться поверх других вьюх. Вы можете загрузить данные, подготовить их к показу, а потом анимированно скрыть LGPlaceholderView.

При инициализации нужно указать вьюху, которую вы будете скрывать:

- (instancetype)initWithView:(UIView *)view;


События можно отлавливать с помощью нотификаций:

kLGPlaceholderViewWillShowNotification;
kLGPlaceholderViewWillDismissNotification;
kLGPlaceholderViewDidShowNotification;
kLGPlaceholderViewDidDismissNotification;


Для показа предусмотрены различные методы, в зависимости от того, какой стиль вы хотите задать:

- (void)showActivityIndicatorAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;
- (void)showActivityIndicatorWithText:(NSString *)text animated:(BOOL)animated completionHandler:(void(^)())completionHandler;
- (void)showProgressViewAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;
- (void)showProgressViewWithText:(NSString *)text animated:(BOOL)animated completionHandler:(void(^)())completionHandler;
- (void)showText:(NSString *)text animated:(BOOL)animated completionHandler:(void(^)())completionHandler;
- (void)showView:(UIView *)view animated:(BOOL)animated completionHandler:(void(^)())completionHandler;


Чтобы скрыть placeholder, нужно вызвать:

- (void)dismissAnimated:(BOOL)animated completionHandler:(void(^)())completionHandler;


Кроме того, различные стили можно комбинировать, если вы вызовите несколько «show» методов подряд, то LGPlaceholderView сменится на другой.
1.png

Программное рисование изображений давно будоражит мой ум. Растровые картинки понемногу отмирают, даже apple в последних версиях xcode добавила поддержку для векторных изображений. Но что если не отягощать приложение дополнительными ресурсами, а рисовать изображения прямо внутри, благо инструменты позволяют.

Плюсы такого подхода, видящиеся мне:

  • Качество изображений всегда на высоте, так как не требуется масштабирование, каждый девайс рисует именно то что нужно ему;
  • Легкое и быстрое изменение содержимого (если, к примеру, необходимо поменять цвет, вместо того чтобы открывать редактор, загружать картинку, изменять цвет и сохранять, достаточно будет всего лишь поменять один параметр в коде);
  • Облегчение веса конечного архива с приложением.


Так и появился LGDrawer. Посмотрим, что он может рисовать на данный момент:

  • Прямоугольник (квадрат)
  • Эллипс (круг)
  • Треугольник
  • Плюс
  • Крест
  • Линия
  • Галочка
  • Стрелочка
  • Сердце
  • Звезда
  • Меню (3 параллельных линии с возможными точками)
  • Различные тени
  • Возможность накладывать изображения друг на друга, или вырезать одни изображения из других


Немного кода
#pragma mark - Rectangle

+ (UIImage *)drawRectangleWithImageSize:(CGSize)imageSize
                                   size:(CGSize)size
                                 offset:(CGPoint)offset
                                 rotate:(CGFloat)degrees
                         roundedCorners:(UIRectCorner)roundedCorners
                           cornerRadius:(CGFloat)cornerRadius
                        backgroundColor:(UIColor *)backgroundColor
                              fillColor:(UIColor *)fillColor
                            strokeColor:(UIColor *)strokeColor
                        strokeThickness:(CGFloat)strokeThickness
                             strokeDash:(NSArray *)strokeDash
                             strokeType:(LGDrawerStrokeType)strokeType
                            shadowColor:(UIColor *)shadowColor
                           shadowOffset:(CGPoint)shadowOffset
                             shadowBlur:(CGFloat)shadowBlur;

#pragma mark - Ellipse

+ (UIImage *)drawEllipseWithImageSize:(CGSize)imageSize
                                 size:(CGSize)size
                               offset:(CGPoint)offset
                               rotate:(CGFloat)degrees
                      backgroundColor:(UIColor *)backgroundColor
                            fillColor:(UIColor *)fillColor
                          strokeColor:(UIColor *)strokeColor
                      strokeThickness:(CGFloat)strokeThickness
                           strokeDash:(NSArray *)strokeDash
                           strokeType:(LGDrawerStrokeType)strokeType
                          shadowColor:(UIColor *)shadowColor
                         shadowOffset:(CGPoint)shadowOffset
                           shadowBlur:(CGFloat)shadowBlur;

#pragma mark - Triangle

/** Stroke type is center */
+ (UIImage *)drawTriangleWithImageSize:(CGSize)imageSize
                                  size:(CGSize)size
                                offset:(CGPoint)offset
                                rotate:(CGFloat)degrees
                          cornerRadius:(CGFloat)cornerRadius
                             direction:(LGDrawerDirection)direction
                       backgroundColor:(UIColor *)backgroundColor
                             fillColor:(UIColor *)fillColor
                           strokeColor:(UIColor *)strokeColor
                       strokeThickness:(CGFloat)strokeThickness
                            strokeDash:(NSArray *)strokeDash
                           shadowColor:(UIColor *)shadowColor
                          shadowOffset:(CGPoint)shadowOffset
                            shadowBlur:(CGFloat)shadowBlur;

#pragma mark - Shadow

+ (UIImage *)drawShadowWithImageSize:(CGSize)imageSize
                           direction:(LGDrawerDirection)direction
                     backgroundColor:(UIColor *)backgroundColor
                         shadowColor:(UIColor *)shadowColor
                        shadowOffset:(CGPoint)shadowOffset
                          shadowBlur:(CGFloat)shadowBlur;

#pragma mark - Plus

+ (UIImage *)drawPlusWithImageSize:(CGSize)imageSize
                              size:(CGSize)size
                            offset:(CGPoint)offset
                            rotate:(CGFloat)degrees
                         thickness:(CGFloat)thickness
                    roundedCorners:(UIRectCorner)roundedCorners
                      cornerRadius:(CGFloat)cornerRadius
                   backgroundColor:(UIColor *)backgroundColor
                         fillColor:(UIColor *)fillColor
                       strokeColor:(UIColor *)strokeColor
                   strokeThickness:(CGFloat)strokeThickness
                        strokeDash:(NSArray *)strokeDash
                        strokeType:(LGDrawerStrokeType)strokeType
                       shadowColor:(UIColor *)shadowColor
                      shadowOffset:(CGPoint)shadowOffset
                        shadowBlur:(CGFloat)shadowBlur;

+ (UIImage *)drawPlusWithImageSize:(CGSize)imageSize
                              size:(CGSize)size
                            offset:(CGPoint)offset
                            rotate:(CGFloat)degrees
                         thickness:(CGFloat)thickness
                   backgroundColor:(UIColor *)backgroundColor
                             color:(UIColor *)color
                              dash:(NSArray *)dash
                       shadowColor:(UIColor *)shadowColor
                      shadowOffset:(CGPoint)shadowOffset
                        shadowBlur:(CGFloat)shadowBlur;

#pragma mark - Cross

+ (UIImage *)drawCrossWithImageSize:(CGSize)imageSize
                               size:(CGSize)size
                             offset:(CGPoint)offset
                             rotate:(CGFloat)degrees
                          thickness:(CGFloat)thickness
                     roundedCorners:(UIRectCorner)roundedCorners
                       cornerRadius:(CGFloat)cornerRadius
                    backgroundColor:(UIColor *)backgroundColor
                          fillColor:(UIColor *)fillColor
                        strokeColor:(UIColor *)strokeColor
                    strokeThickness:(CGFloat)strokeThickness
                         strokeDash:(NSArray *)strokeDash
                         strokeType:(LGDrawerStrokeType)strokeType
                        shadowColor:(UIColor *)shadowColor
                       shadowOffset:(CGPoint)shadowOffset
                         shadowBlur:(CGFloat)shadowBlur;

+ (UIImage *)drawCrossWithImageSize:(CGSize)imageSize
                               size:(CGSize)size
                             offset:(CGPoint)offset
                             rotate:(CGFloat)degrees
                          thickness:(CGFloat)thickness
                    backgroundColor:(UIColor *)backgroundColor
                              color:(UIColor *)color
                               dash:(NSArray *)dash
                        shadowColor:(UIColor *)shadowColor
                       shadowOffset:(CGPoint)shadowOffset
                         shadowBlur:(CGFloat)shadowBlur;

#pragma mark - Line

+ (UIImage *)drawLineWithImageSize:(CGSize)imageSize
                              length:(CGFloat)length
                            offset:(CGPoint)offset
                            rotate:(CGFloat)degrees
                         thickness:(CGFloat)thickness
                         direction:(LGDrawerLineDirection)direction
                   backgroundColor:(UIColor *)backgroundColor
                             color:(UIColor *)color
                              dash:(NSArray *)dash
                       shadowColor:(UIColor *)shadowColor
                      shadowOffset:(CGPoint)shadowOffset
                        shadowBlur:(CGFloat)shadowBlur;

#pragma mark - Tick

+ (UIImage *)drawTickWithImageSize:(CGSize)imageSize
                              size:(CGSize)size
                            offset:(CGPoint)offset
                            rotate:(CGFloat)degrees
                         thickness:(CGFloat)thickness
                   backgroundColor:(UIColor *)backgroundColor
                             color:(UIColor *)color
                              dash:(NSArray *)dash
                       shadowColor:(UIColor *)shadowColor
                      shadowOffset:(CGPoint)shadowOffset
                        shadowBlur:(CGFloat)shadowBlur;

#pragma mark - Arrow

+ (UIImage *)drawArrowWithImageSize:(CGSize)imageSize
                               size:(CGSize)size
                             offset:(CGPoint)offset
                             rotate:(CGFloat)degrees
                          thickness:(CGFloat)thickness
                          direction:(LGDrawerDirection)direction
                    backgroundColor:(UIColor *)backgroundColor
                              color:(UIColor *)color
                               dash:(NSArray *)dash
                        shadowColor:(UIColor *)shadowColor
                       shadowOffset:(CGPoint)shadowOffset
                         shadowBlur:(CGFloat)shadowBlur;

+ (UIImage *)drawArrowTailedWithImageSize:(CGSize)imageSize
                                     size:(CGSize)size
                                   offset:(CGPoint)offset
                                   rotate:(CGFloat)degrees
                                thickness:(CGFloat)thickness
                                direction:(LGDrawerDirection)direction
                          backgroundColor:(UIColor *)backgroundColor
                                    color:(UIColor *)color
                                     dash:(NSArray *)dash
                              shadowColor:(UIColor *)shadowColor
                             shadowOffset:(CGPoint)shadowOffset
                               shadowBlur:(CGFloat)shadowBlur;

#pragma mark - Heart

/** Stroke type is center */
+ (UIImage *)drawHeartWithImageSize:(CGSize)imageSize
                               size:(CGSize)size
                             offset:(CGPoint)offset
                             rotate:(CGFloat)degrees
                    backgroundColor:(UIColor *)backgroundColor
                          fillColor:(UIColor *)fillColor
                        strokeColor:(UIColor *)strokeColor
                    strokeThickness:(CGFloat)strokeThickness
                         strokeDash:(NSArray *)strokeDash
                        shadowColor:(UIColor *)shadowColor
                       shadowOffset:(CGPoint)shadowOffset
                         shadowBlur:(CGFloat)shadowBlur;

#pragma mark - Star

/** Stroke type is center */
+ (UIImage *)drawStarWithImageSize:(CGSize)imageSize
                              size:(CGSize)size
                            offset:(CGPoint)offset
                            rotate:(CGFloat)degrees
                   backgroundColor:(UIColor *)backgroundColor
                         fillColor:(UIColor *)fillColor
                       strokeColor:(UIColor *)strokeColor
                   strokeThickness:(CGFloat)strokeThickness
                        strokeDash:(NSArray *)strokeDash
                       shadowColor:(UIColor *)shadowColor
                      shadowOffset:(CGPoint)shadowOffset
                        shadowBlur:(CGFloat)shadowBlur;

#pragma mark - Menu

+ (UIImage *)drawMenuWithImageSize:(CGSize)imageSize
                              size:(CGSize)size
                            offset:(CGPoint)offset
                            rotate:(CGFloat)degrees
                         thickness:(CGFloat)thickness
                            dotted:(BOOL)dotted
                      dotsPosition:(LGDrawerMenuDotsPosition)dotsPosition
                  dotsCornerRadius:(CGFloat)dotsCornerRadius
                 linesCornerRadius:(CGFloat)linesCornerRadius
                   backgroundColor:(UIColor *)backgroundColor
                         fillColor:(UIColor *)fillColor
                       strokeColor:(UIColor *)strokeColor
                   strokeThickness:(CGFloat)strokeThickness
                        strokeDash:(NSArray *)strokeDash
                       shadowColor:(UIColor *)shadowColor
                      shadowOffset:(CGPoint)shadowOffset
                        shadowBlur:(CGFloat)shadowBlur;

#pragma mark - Images

+ (UIImage *)drawImage:(UIImage *)image1
               onImage:(UIImage *)image2
                 clear:(BOOL)clear;

+ (UIImage *)drawImageOnImage:(NSArray *)images;

+ (UIImage *)drawImagesWithFinishSize:(CGSize)finishSize
                               image1:(UIImage *)image1
                           image1Rect:(CGRect)rect1
                               image2:(UIImage *)image2
                           image2Rect:(CGRect)rect2
                                clear:(BOOL)clear;

+ (UIImage *)drawImagesWithFinishSize:(CGSize)finishSize
                               image1:(UIImage *)image1
                         image1Offset:(CGPoint)offset1
                               image2:(UIImage *)image2
                         image2Offset:(CGPoint)offset2
                                clear:(BOOL)clear;



Принцип рисования следующий. У каждого метода есть параметры, где вы можете задавать размер области (холста), в которой будет находиться изображения, её заливку; размер самого изображения, его заливку, обводку, тень, смещение относительно центра внутри холста, угол поворота изображения и, если возможно, толщину линий и закругления углов.
Не все параметры удалось реализовать для каждого метода, но старался по максимуму возможного.

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


Скриншоты


Часто хочется расширить функционал той или иной вьюхи. Почему для UILabel нельзя задать contentEdgeInsets? Если я хочу расположить UILabel поверх картинки, то для удобства чтения текста, вместо того, чтобы расширить background, приходится создавать дополнительную UIView. Или для UIButton, почему для каждого состояния можно задать текст, цвета текста, картинку, картинку background’a, но банального цвета background’a задать нельзя. А что насчет выбора расположения картинки относительно текста?

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

  • contentEdgeInsets для UILabel
  • backgroundColor для разных состояний UIButton
  • Возможность задать UIButton не прямоугольную форму за счет масок
  • Выбрать расположение картинки относительно текста в UIButton
  • Задание максимальной длины текста для UITextField и UITextView
  • contentEdgeInsets для текста и боковых изображений в UITextField
  • Удаление лишних пробелов и переносов строк из UITextField
  • Возможность авторасширения для UITextView с заданием максимальной высоты или количества строк
  • placeholder для UITextView


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

Краткий список:

  • Добавил UIScrollViewController и UIWebViewController
  • Для UITableViewController, UICollectionViewController и UIScrollViewController добавил LGRefreshView и LGPlaceholderView; для UIWebViewController только LGPlaceholderView
  • Для UITableViewControlle добавил новый метод делегата «heightForRowAtIndexPathAsync», который позволяет асинхронно рассчитывать высоту ячеек. То есть, если у вас динамическая высота ячеек и вы часто подгружаете список, данный метод поможет избежать задержек в интерфейсе
  • Для UICollectionViewController попытался упростить инициализацию layout’а, чтобы можно было настроить сетку без лишних рассчетов
  • UIWebViewController можно использовать без наследования, а сразу передавать в тело ссылку на нужный ресурс при инициализации
  • Добавил методы для автоматического слежения за клавиатурой, то есть contentInsets будут меняться при появлении и исчезновении клавиатуры


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

Обработка изображений, конвертация цветов, использование масок, отправка электронных писем, вызов звонка с подтверждением и без, показ местоположения на карте, узнать состояние подключения к интернету, добавить человека в адресную книгу, закодировать данные, получить MD5 и SHA1 хэши, добавить событие в календарь, получить изображение с камеры… и многое многое другое, что я не буду здесь перечислять. Думаю названия всех методов и переменных должны быть более-менее интуитивно понятны, поэтому, чтобы ознакомиться с полным списком возможностей, предлагаю просмотреть header файлы исходников.


Эти 3 библиотеки, на мой взгляд, не так интересны как остальные. LGSharing ясное дело помогает постить в социальные сети (ВКонтакте, Facebook и Twitter) + отправлять сообщения на email и sms. LGConnection является оберткой вокруг AFNetworking, из коробки может парсить ответ от сервера включая XML формат + имеет логику для обработки прерывания интернет соединения. LGAudioStreamHelper помогает работать с аудио стримами, определять формат, получать метаданные и записывать поток. Более подробно рассказывать не буду, если кому интересно, заходите на гитхаб и пробуйте.
Всем спасибо за внимание. Буду очень рад если мои труды кому-то принесут пользу. Открыт для объективной критики, предложениям по улучшению или расширению функционала библиотек. Если есть какие вопросы — задавайте, постараюсь ответить.

© Habrahabr.ru