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

Вступление


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



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

Similar posts

Comments 27

    +2
    Очень качественный и удобный джентельменский набор! Благодарю вас)
      0
      Рад стараться :)
      +2
      Еще не программировал под iOS, но всё равно спасибо!
      Колоссальный и полезный труд.
      Вот бы так все разрабочики и компании делали при смене/прекращении деятельности, а не прятали полезные наработки гнить в дальний угол жестких дисков.
      • UFO just landed and posted this here
          0
          Присоединяюсь к вопросу.
            0
            Думаю за 5 лет надоело.
              +1
              Если автор под вебом понимает нечто серьезное, то его можно понять. Но если же имеется ввиду всякие там css, js, php, то это явно шаг назад. Отсюда и вопрос.
                0
                В профиле автора написанно, что RoR. NayZaK, это шаг назад?
                Friend_LGA 20, спасибо за статью.
                  0
                  Опять же смотря что делать на этих рельсах. Но в целом пять лет воевать с xcode, вникать в тонкости obj-c, изучать эпловские фрэймворки, бороться за каждый мегабайт памяти, за отсутствие лагов и падений, продумывать архитектуру приложения, чтобы вот так вот взять и метнуться на рельсы? Странно это. Я к тому, что человек долгое время изучал инструмент и приучал себя к оптимизациям, а теперь внезапно рельсы. Они-то уж точно не про оптимизацию. Ровно как и другие вебовские приблуды для скриптовых языков.
                    0
                    Серверный код, который обслуживает миллионы пользователей может быть интереснее с архитектурной точки зрения. Особенно, если objc и клиентская разработка банально надоела.
              0
              Если это действительно кому-то интересно, то постараюсь сформулировать…
              Работа для меня (по крайней мере сейчас) — это скорее способ саморазвития и поиска себя. Когда я начал заниматься iOS, мобильный рынок, впрочем как и сейчас, был очень быстрорастущим и хотелось ухватить свой кусок пирога. Но оказалось, что все не так просто, как казалось. Если брать в расчет чисто мобильное приложение, без какого-либо бэкграунда, то что это может быть? Игра или оффлайн-приложение типа «купи батон». Да, таких много, и встречаются действительно поражающие экземпляры, например как «Flappy Bird», но это скорее исключение из правил. Поработав в индустрии, ко мне пришло понимание, что если делать что-то серьезное, то мобильное приложение это скорее дополнение, компаньон, для внешнего сервиса. А внешний сервис это что? В основном это веб сайты. Поэтому решил идти глубже и изучать веб.
              Почему Ruby on Rails? Ну… в нашем городе не сказать чтобы очень большой выбор, а поработать в команде со знающими людьми, это уже не плохое подспорье. Все равно главное — это понять суть, внутреннее устройство, как связываются между собой frontend и backend, а рельсы это просто инструмент, коих много, и чтобы выбрать то что нравится нужно пробовать.
              Не претендую на истину в последней инстанции, все что сказал выше это мое ИМХО, прошу не принимать сильно близко к сердцу.
              0
              Спасибо! Есть очень крутые вещи.
                0
                Пожалуйста, пользуйтесь на здоровье! :)
                +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
                  0
                  В 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 с анимациями очень плохо влияют на производительность

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

                  Насчет остального спасибо, учту, в ближайшее время постараюсь исправить
                    0
                    — (UIView *)leftView { return _leftView; } – почему не readonly property?

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

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

                    А можно в LGSharing добавить инстаграм еще?
                      0
                      Пожалуйста!
                      Насчет LGSharing — как я уже писал в статье, от iOS разработки я отошел и сейчас занимаюсь вебом, поэтому времени добавлять новый функционал сейчас у меня нет, готов только править критичные баги. Но pull-request'ы на гитхабе приветствуются :)
                      0
                      Занимаюсь разработкой только год… Это прекрасная подборка Ваших наработок. Большое спасибо!
                        0
                        Пожалуйста, удачи вам на этом тернистом пути! :)
                        +1
                        Спасибо! Мы вот с веба в iOs :) Про веб не забываем, правда!
                          0
                          Спасибо за шаринг, рекомендую Вам поделиться вашими подсами на Cocoa Controls :) Распиарите так свой гитхаб, например :)
                            0
                            Спасибо за совет, обязательно им воспользуюсь!
                            0
                            Поддержка Carthage будет в плюс.
                              0
                              Немного с запазданием, но сделал :)
                              0
                              UIRefreshControl теперь из коробки работает с коллекшен вью.

                              Only users with full accounts can post comments. Log in, please.