Вступление


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

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

LGAlertView


Скриншоты


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<LGAlertViewDelegate> 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. При появлении новых старые будут исчезать, а при исчезновении — появляться.

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<LGActionSheetDelegate> 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;


LGSideMenuController


Скриншоты


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

Краткий список возможностей:
  • Поддерживается как левое меню, так и правое
  • Различные виды анимаций
  • Показ и скрытие по нажатию кнопки и по жесту
  • Настройка правил показа для разных девайсов и ориентаций (например можно сделать меню нескрываемым для 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;

LGPlusButtonsView


Скриншоты


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

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

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

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

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

Немного кода
Делегирование:

@property (assign, nonatomic) id<LGPlusButtonsViewDelegate> 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


Скриншоты


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

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

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

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

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

Немного кода
Делегирование:
@property (assign, nonatomic) id<LGFilterViewDelegate> 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;


LGRefreshView


Скриншоты


«Потяни, чтобы обновить» — очень модная фича, которая есть практически в каждом приложении. Даже 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<LGRefreshViewDelegate> delegate;

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

Блоки:

@property (strong, nonatomic) void (^refreshHandler)(LGRefreshView *refreshView);

NSNotifications:

kLGRefreshViewBeginRefreshingNotification;
kLGRefreshViewEndRefreshingNotification;


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

- (void)endRefreshing;

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

- (void)triggerAnimated:(BOOL)animated;


LGPlaceholderView


Скриншоты


Если у вас клиент-серверное приложение, то при переходе на новый контроллер часто приходится загружать данные. Чтобы не фризить 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 сменится на другой.

LGDrawer




Программное рисование изображений давно будоражит мой ум. Растровые картинки понемногу отмирают, даже 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;


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

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

LGViews


Скриншоты


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

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

LGViewControllers


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

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

LGHelper, LGHelper+NS, LGHelper+UI


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

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

LGSharing, LGAudioStreamHelper, LGConnection


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



Всем спасибо за внимание. Буду очень рад если мои труды кому-то принесут пользу. Открыт для объективной критики, предложениям по улучшению или расширению функционала библиотек. Если есть какие вопросы — задавайте, постараюсь ответить.

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


  1. i_user
    14.07.2015 19:42
    +2

    Очень качественный и удобный джентельменский набор! Благодарю вас)


    1. Friend_LGA Автор
      14.07.2015 21:02

      Рад стараться :)


  1. Error1024
    15.07.2015 00:18
    +2

    Еще не программировал под iOS, но всё равно спасибо!
    Колоссальный и полезный труд.
    Вот бы так все разрабочики и компании делали при смене/прекращении деятельности, а не прятали полезные наработки гнить в дальний угол жестких дисков.


  1. InstaRobot
    15.07.2015 02:24
    +3

    А что не так с iOS-разработкой?


    1. NayZaK
      15.07.2015 02:30

      Присоединяюсь к вопросу.


    1. Headmast
      15.07.2015 09:15

      Думаю за 5 лет надоело.


      1. NayZaK
        15.07.2015 09:22
        +1

        Если автор под вебом понимает нечто серьезное, то его можно понять. Но если же имеется ввиду всякие там css, js, php, то это явно шаг назад. Отсюда и вопрос.


        1. namc
          15.07.2015 11:45

          В профиле автора написанно, что RoR. NayZaK, это шаг назад?
          Friend_LGA 20, спасибо за статью.


          1. NayZaK
            15.07.2015 12:32

            Опять же смотря что делать на этих рельсах. Но в целом пять лет воевать с xcode, вникать в тонкости obj-c, изучать эпловские фрэймворки, бороться за каждый мегабайт памяти, за отсутствие лагов и падений, продумывать архитектуру приложения, чтобы вот так вот взять и метнуться на рельсы? Странно это. Я к тому, что человек долгое время изучал инструмент и приучал себя к оптимизациям, а теперь внезапно рельсы. Они-то уж точно не про оптимизацию. Ровно как и другие вебовские приблуды для скриптовых языков.


            1. esc
              15.07.2015 17:27

              Серверный код, который обслуживает миллионы пользователей может быть интереснее с архитектурной точки зрения. Особенно, если objc и клиентская разработка банально надоела.


    1. Friend_LGA Автор
      16.07.2015 22:02

      Если это действительно кому-то интересно, то постараюсь сформулировать…
      Работа для меня (по крайней мере сейчас) — это скорее способ саморазвития и поиска себя. Когда я начал заниматься iOS, мобильный рынок, впрочем как и сейчас, был очень быстрорастущим и хотелось ухватить свой кусок пирога. Но оказалось, что все не так просто, как казалось. Если брать в расчет чисто мобильное приложение, без какого-либо бэкграунда, то что это может быть? Игра или оффлайн-приложение типа «купи батон». Да, таких много, и встречаются действительно поражающие экземпляры, например как «Flappy Bird», но это скорее исключение из правил. Поработав в индустрии, ко мне пришло понимание, что если делать что-то серьезное, то мобильное приложение это скорее дополнение, компаньон, для внешнего сервиса. А внешний сервис это что? В основном это веб сайты. Поэтому решил идти глубже и изучать веб.
      Почему Ruby on Rails? Ну… в нашем городе не сказать чтобы очень большой выбор, а поработать в команде со знающими людьми, это уже не плохое подспорье. Все равно главное — это понять суть, внутреннее устройство, как связываются между собой frontend и backend, а рельсы это просто инструмент, коих много, и чтобы выбрать то что нравится нужно пробовать.
      Не претендую на истину в последней инстанции, все что сказал выше это мое ИМХО, прошу не принимать сильно близко к сердцу.


  1. AlexGx
    15.07.2015 04:27

    Спасибо! Есть очень крутые вещи.


    1. Friend_LGA Автор
      16.07.2015 22:05

      Пожалуйста, пользуйтесь на здоровье! :)


  1. storoj
    15.07.2015 04:34
    +1

    • В LGDrawer какой-нибудь билдер бы, чтобы простые фигуры можно было сконфигурировать всего парой параметров, а остальные были бы по-умолчанию нулями. Легко было бы добавлять новые настройки фигур, не меняя весь пользовательский код.
    • Вместо __attribute__((unavailable)) наверное стоило бы заюзать NS_DESIGNATED_INITIALIZER
    • NS_ENUM для красоты
    • if (_type == LGPlaceholderViewTypeText) {… } else if (_type == LGPlaceholderViewTypeActivityIndicator) { ...} else if ...
    • /** Do not forget about weak referens to self */ – а на не self значит можно болт забить? от этого «self» у меня аж болит всё
    • cornerRadius+masksToBounds+shadowOffset с анимациями очень плохо влияют на производительность
    • — (UIView *)leftView { return _leftView; } – почему не readonly property?
    • animateStandart… -> animateStandard


    1. Friend_LGA Автор
      16.07.2015 22:21

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

      Насчет «switch — case» коллеги пинают уже не первый год, но все никак не могу постичь дзен и начать его использовать, спасибо за очередной пинок :)

      /** Do not forget about weak referens to self */ – а на не self значит можно болт забить? от этого «self» у меня аж болит всё

      Не совсем понял… Когда блок задан как strong, то если мы внутри него обратимся к self — получим retain cycle. Чтобы этого избежать нужно делать как-то так:
      __weak typeof(self) wself = self;
      block = ^(void)
      {
          if (wself)
          {
              __strong typeof(wself) self = wself;
      
              ...
          }
      };
      

      cornerRadius+masksToBounds+shadowOffset с анимациями очень плохо влияют на производительность

      Конечно нужно использовать с умом. Но в целом для современных девайсов все не так критично как раньше.

      Насчет остального спасибо, учту, в ближайшее время постараюсь исправить


      1. Friend_LGA Автор
        16.07.2015 22:26

        — (UIView *)leftView { return _leftView; } – почему не readonly property?

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


      1. Pr0Ger
        17.07.2015 00:05

        Не совсем понял… Когда блок задан как strong, то если мы внутри него обратимся к self — получим retain cycle. Чтобы этого избежать нужно делать как-то так:

        Это конечно да, просто retain цикл можно не только из-за self получать; так-же как можно использовать self внутри блока без weak ссылки и не получить утечек памяти


  1. askl
    15.07.2015 04:57
    +1

    Отличная подборка! Спасибо!

    А можно в LGSharing добавить инстаграм еще?


    1. Friend_LGA Автор
      16.07.2015 22:37

      Пожалуйста!
      Насчет LGSharing — как я уже писал в статье, от iOS разработки я отошел и сейчас занимаюсь вебом, поэтому времени добавлять новый функционал сейчас у меня нет, готов только править критичные баги. Но pull-request'ы на гитхабе приветствуются :)


  1. DenFav
    15.07.2015 12:10

    Занимаюсь разработкой только год… Это прекрасная подборка Ваших наработок. Большое спасибо!


    1. Friend_LGA Автор
      16.07.2015 22:37

      Пожалуйста, удачи вам на этом тернистом пути! :)


  1. Iforgot
    15.07.2015 15:50
    +1

    Спасибо! Мы вот с веба в iOs :) Про веб не забываем, правда!


  1. Lonkly
    15.07.2015 15:54

    Спасибо за шаринг, рекомендую Вам поделиться вашими подсами на Cocoa Controls :) Распиарите так свой гитхаб, например :)


    1. Friend_LGA Автор
      16.07.2015 22:40

      Спасибо за совет, обязательно им воспользуюсь!


  1. Anakros
    15.07.2015 17:34

    Поддержка Carthage будет в плюс.


  1. Voley
    21.07.2015 13:23

    UIRefreshControl теперь из коробки работает с коллекшен вью.