#import "TGSearchBar.h" #import "TGHacks.h" #import "TGImageUtils.h" #import "TGStringUtils.h" #import "TGButtonGroupView.h" #import "TGFont.h" #import "TGModernButton.h" #import "TGTimerTarget.h" @interface TGSearchBar () { CGFloat _cancelButtonWidth; } @property (nonatomic, strong) UIView *wrappingClip; @property (nonatomic, strong) UIView *wrappingView; @property (nonatomic, strong) UIImageView *textFieldBackground; @property (nonatomic, strong) UIImage *normalTextFieldBackgroundImage; @property (nonatomic, strong) UIImage *activeTextFieldBackgroundImage; @property (nonatomic, strong) TGModernButton *customCancelButton; @property (nonatomic) bool showsCustomCancelButton; @property (nonatomic, strong) UILabel *placeholderLabel; @property (nonatomic, strong) UIImageView *customSearchIcon; @property (nonatomic, strong) UIActivityIndicatorView *customSearchActivityIndicator; @property (nonatomic, strong) NSTimer *searchActivityTimer; @property (nonatomic, strong) UIButton *customClearButton; @property (nonatomic, strong) UIView *customScopeButtonContainer; @property (nonatomic, strong) UISegmentedControl *customSegmentedControl; @property (nonatomic) int customCurrentScope; @end @implementation TGSearchBar + (CGFloat)searchBarBaseHeight { return 44.0f; } - (CGFloat)baseHeight { if (self.showsScopeBar) return 44.0f; return [self inputHeight] + 12.0f; } - (CGFloat)inputContentOffset { return _style == TGSearchBarStyleLightAlwaysPlain ? 5.0f : 0.0f; } - (CGFloat)searchIconOffset { return _style == TGSearchBarStyleLightAlwaysPlain ? TGRetinaPixel : 0.0f; } - (CGFloat)inputHeight { return _style == TGSearchBarStyleLightAlwaysPlain ? 40.0f : 28.0f; } + (CGFloat)searchBarScopeHeight { return 44.0f; } - (CGFloat)topPadding { return -1.0f; } - (id)initWithFrame:(CGRect)frame { return [self initWithFrame:frame style:TGSearchBarStyleLightPlain]; } - (instancetype)initWithFrame:(CGRect)frame style:(TGSearchBarStyle)style { self = [super initWithFrame:frame]; if (self != nil) { _delayActivity = true; _wrappingClip = [[UIView alloc] initWithFrame:CGRectMake(0.0f, -20.0f, frame.size.width, frame.size.height + 20.0f)]; _wrappingClip.clipsToBounds = true; [self addSubview:_wrappingClip]; _wrappingView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 20.0f, frame.size.width, frame.size.height)]; [_wrappingClip addSubview:_wrappingView]; _style = style; NSString *backgroundFileName = nil; NSString *backgroundActiveFileName = nil; UIImage *backgroundManualImage = nil; UIImage *backgroundManualActiveImage = nil; if (_style == TGSearchBarStyleDefault) { backgroundFileName = @"SearchBarBackground.png"; backgroundActiveFileName = @"SearchBarBackground_Active.png"; } else if (_style == TGSearchBarStyleDark) { backgroundFileName = @"SearchBarBackgroundDark.png"; backgroundActiveFileName = @"SearchBarBackgroundDark.png"; } else if (_style == TGSearchBarStyleLight || _style == TGSearchBarStyleLightPlain || _style == TGSearchBarStyleLightAlwaysPlain || _style == TGSearchBarStyleHeader) { static UIImage *image = nil; static UIImage *imagePlain = nil; static UIImage *imageHeader = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { UIGraphicsBeginImageContextWithOptions(CGSizeMake(1.0f, 3.0f), true, 0.0f); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); CGContextFillRect(context, CGRectMake(0.0f, 0.0f, 1.0f, 3.0f)); imagePlain = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:0 topCapHeight:1]; CGContextSetFillColorWithColor(context, UIColorRGB(0xc8c7cc).CGColor); CGFloat separatorHeight = TGIsRetina() ? 0.5f : 1.0f; CGContextFillRect(context, CGRectMake(0.0f, 3.0f - separatorHeight, 1.0f, separatorHeight)); image = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:0 topCapHeight:1]; UIGraphicsEndImageContext(); UIGraphicsBeginImageContextWithOptions(CGSizeMake(1.0f, 3.0f), true, 0.0f); context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, UIColorRGB(0xf7f7f7).CGColor); CGContextFillRect(context, CGRectMake(0.0f, 0.0f, 1.0f, 3.0f)); CGContextSetFillColorWithColor(context, UIColorRGB(0xc8c7cc).CGColor); CGContextFillRect(context, CGRectMake(0.0f, 3.0f - separatorHeight, 1.0f, separatorHeight)); imageHeader = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:0 topCapHeight:1]; UIGraphicsEndImageContext(); }); if (_style == TGSearchBarStyleHeader) { backgroundManualImage = imageHeader; backgroundManualActiveImage = imageHeader; } else { backgroundManualImage = _style == TGSearchBarStyleLight ? image : imagePlain; backgroundManualActiveImage = _style == TGSearchBarStyleLightAlwaysPlain ? imagePlain : image; } } UIImage *backgroundImage = nil; if (backgroundManualImage != nil) backgroundImage = backgroundManualImage; else backgroundImage = [[UIImage imageNamed:backgroundFileName] stretchableImageWithLeftCapWidth:1 topCapHeight:1]; _customBackgroundView = [[UIImageView alloc] initWithFrame:self.bounds]; _customBackgroundView.image = backgroundImage; _customBackgroundView.userInteractionEnabled = true; [_customBackgroundView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backgroundTapGesture:)]]; [_wrappingView addSubview:_customBackgroundView]; UIImage *activeBackgroundImage = nil; if (backgroundManualActiveImage != nil) activeBackgroundImage = backgroundManualActiveImage; else activeBackgroundImage = [[UIImage imageNamed:backgroundActiveFileName] stretchableImageWithLeftCapWidth:1 topCapHeight:1]; _customActiveBackgroundView = [[UIImageView alloc] initWithFrame:self.bounds]; _customActiveBackgroundView.image = activeBackgroundImage; _customActiveBackgroundView.alpha = 0.0f; [_wrappingView addSubview:_customActiveBackgroundView]; _textFieldBackground = [[UIImageView alloc] initWithImage:self.normalTextFieldBackgroundImage]; _textFieldBackground.userInteractionEnabled = false; [_wrappingView addSubview:_textFieldBackground]; UIColor *placeholderColor = nil; if (_style == TGSearchBarStyleDefault) placeholderColor = UIColorRGB(0x8e8e93); else if (_style == TGSearchBarStyleDark) placeholderColor = [UIColor whiteColor]; else if (_style == TGSearchBarStyleLight || _style == TGSearchBarStyleLightPlain || _style == TGSearchBarStyleLightAlwaysPlain || _style == TGSearchBarStyleHeader) placeholderColor = UIColorRGB(0x8e8e93); _placeholderLabel = [[UILabel alloc] init]; _placeholderLabel.textAlignment = NSTextAlignmentLeft; _placeholderLabel.userInteractionEnabled = false; _placeholderLabel.textColor = placeholderColor; _placeholderLabel.backgroundColor = [UIColor clearColor]; _placeholderLabel.font = TGSystemFontOfSize(style == TGSearchBarStyleLightAlwaysPlain ? 16.0f : 14.0f); _placeholderLabel.text = TGLocalized(@"Common.Search"); [_wrappingView addSubview:_placeholderLabel]; NSString *iconFileName = nil; if (_style == TGSearchBarStyleDefault) iconFileName = @"SearchBarIcon.png"; else if (_style == TGSearchBarStyleDark) iconFileName = @"SearchBarIconDark.png"; else if (_style == TGSearchBarStyleLight || _style == TGSearchBarStyleLightPlain || _style == TGSearchBarStyleHeader) iconFileName = @"SearchBarIconLight.png"; else if (_style == TGSearchBarStyleLightAlwaysPlain) iconFileName = @"SearchBarIconLightLarge.png"; _customSearchIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:iconFileName]]; _customSearchIcon.userInteractionEnabled = false; [_wrappingView addSubview:_customSearchIcon]; _customSearchActivityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:4]; _customSearchActivityIndicator.alpha = 0.4f; _customSearchActivityIndicator.userInteractionEnabled = false; _customSearchActivityIndicator.hidden = true; [_wrappingView addSubview:_customSearchActivityIndicator]; } return self; } - (void)setAlwaysExtended:(bool)alwaysExtended { if (_alwaysExtended != alwaysExtended) { _alwaysExtended = alwaysExtended; [self layoutSubviews]; } } - (void)setFrame:(CGRect)frame { [super setFrame:frame]; } - (void)sizeToFit { CGFloat requiredHeight = 0; if (_searchBarShouldShowScopeControl && ![self landscapeMode] && !_scopeBarCollapsed) { requiredHeight = [self baseHeight] + [TGSearchBar searchBarScopeHeight]; } else { requiredHeight = [self baseHeight]; } CGRect frame = self.frame; frame.size.height = requiredHeight; self.frame = frame; } - (BOOL)showsCancelButton { return _showsCustomCancelButton; } - (UIImage *)normalTextFieldBackgroundImage { if (_normalTextFieldBackgroundImage == nil) { NSString *fileName = nil; if (_style == TGSearchBarStyleDefault) fileName = @"SearchInputField.png"; else if (_style == TGSearchBarStyleDark) fileName = @"SearchInputFieldDark.png"; else if (_style == TGSearchBarStyleLight || _style == TGSearchBarStyleLightPlain) fileName = @"SearchInputFieldLight.png"; else if (_style == TGSearchBarStyleLightAlwaysPlain) { static UIImage *image = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { CGFloat diameter = 10.0f; UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, UIColorRGB(0xe8e8e8).CGColor); CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter)); image = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:(NSInteger)(diameter / 2.0f) topCapHeight:(NSInteger)(diameter / 2.0f)]; UIGraphicsEndImageContext(); }); _normalTextFieldBackgroundImage = image; return _normalTextFieldBackgroundImage; } else if (_style == TGSearchBarStyleHeader) { static UIImage *headerImage = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { CGFloat diameter = 10.0f; UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, UIColorRGB(0xe4e4e4).CGColor); CGContextFillEllipseInRect(context, CGRectMake(0.0f, 0.0f, diameter, diameter)); headerImage = [UIGraphicsGetImageFromCurrentImageContext() stretchableImageWithLeftCapWidth:(NSInteger)(diameter / 2.0f) topCapHeight:(NSInteger)(diameter / 2.0f)]; UIGraphicsEndImageContext(); }); _normalTextFieldBackgroundImage = headerImage; return _normalTextFieldBackgroundImage; } UIImage *rawImage = [UIImage imageNamed:fileName]; _normalTextFieldBackgroundImage = [rawImage stretchableImageWithLeftCapWidth:(int)(rawImage.size.width / 2) topCapHeight:(int)(rawImage.size.height / 2)]; } return _normalTextFieldBackgroundImage; } - (UIImage *)activeTextFieldBackgroundImage { if (_activeTextFieldBackgroundImage == nil) { NSString *fileName = nil; if (_style == TGSearchBarStyleDefault) fileName = @"SearchInputField_Active.png"; else if (_style == TGSearchBarStyleDark) fileName = @"SearchInputFieldDark.png"; else if (_style == TGSearchBarStyleLight || _style == TGSearchBarStyleLightPlain) fileName = @"SearchInputFieldLight.png"; else if (_style == TGSearchBarStyleLightAlwaysPlain || _style == TGSearchBarStyleHeader) { _activeTextFieldBackgroundImage = [self normalTextFieldBackgroundImage]; return _activeTextFieldBackgroundImage; } UIImage *rawImage = [UIImage imageNamed:fileName]; _activeTextFieldBackgroundImage = [rawImage stretchableImageWithLeftCapWidth:(int)(rawImage.size.width / 2) topCapHeight:(int)(rawImage.size.height / 2)]; } return _activeTextFieldBackgroundImage; } - (UITextField *)maybeCustomTextField { return _customTextField; } - (UITextField *)customTextField { if (_customTextField == nil) { CGRect frame = _textFieldBackground.frame; frame.origin.y -= TGIsRetina() ? 0.0f : 0.0f; frame.origin.x += 27; frame.size.width -= 27 + 8 + 14; _customTextField = [[UITextField alloc] initWithFrame:frame]; _customTextField.font = _placeholderLabel.font; if (iosMajorVersion() >= 7) _customTextField.textAlignment = NSTextAlignmentNatural; _customTextField.autocapitalizationType = UITextAutocapitalizationTypeNone; _customTextField.autocorrectionType = UITextAutocorrectionTypeNo; UIColor *textColor = nil; UIImage *clearImage = nil; if (_style == TGSearchBarStyleDefault || _style == TGSearchBarStyleLight || _style == TGSearchBarStyleLightPlain || _style == TGSearchBarStyleLightAlwaysPlain || _style == TGSearchBarStyleHeader) { textColor = [UIColor blackColor]; clearImage = [UIImage imageNamed:@"SearchBarClearIcon.png"]; } else if (_style == TGSearchBarStyleDark) { textColor = [UIColor whiteColor]; clearImage = [UIImage imageNamed:@"SearchBarClearIconDark.png"]; } _customTextField.textColor = textColor; _customTextField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; _customTextField.returnKeyType = UIReturnKeySearch; _customTextField.keyboardAppearance = _style == TGSearchBarStyleDark ? UIKeyboardAppearanceAlert : UIKeyboardAppearanceDefault; _customTextField.delegate = self; [_customTextField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged]; _customClearButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, clearImage.size.width, clearImage.size.height)]; [_customClearButton setBackgroundImage:clearImage forState:UIControlStateNormal]; [_customClearButton addTarget:self action:@selector(customClearButtonPressed) forControlEvents:UIControlEventTouchUpInside]; _customClearButton.hidden = true; [_wrappingView addSubview:_customTextField]; [_wrappingView addSubview:_customClearButton]; [self setNeedsLayout]; } return _customTextField; } - (UIButton *)customCancelButton { if (_customCancelButton == nil) { _cancelButtonWidth = [TGLocalized(@"Common.Cancel") sizeWithFont:TGSystemFontOfSize(17.0f)].width + 11.0f; CGRect textFieldBackgroundFrame = _textFieldBackground.frame; _customCancelButton = [[TGModernButton alloc] initWithFrame:CGRectMake(textFieldBackgroundFrame.origin.x + textFieldBackgroundFrame.size.width + 10, 0, _cancelButtonWidth, [self baseHeight])]; [_customCancelButton setTitle:TGLocalized(@"Common.Cancel") forState:UIControlStateNormal]; UIColor *buttonColor = nil; if (_style == TGSearchBarStyleDefault || _style == TGSearchBarStyleLight || _style == TGSearchBarStyleLightPlain || _style == TGSearchBarStyleLightAlwaysPlain || _style == TGSearchBarStyleHeader) buttonColor = TGAccentColor(); else if (_style == TGSearchBarStyleDark) buttonColor = [UIColor whiteColor]; [_customCancelButton setTitleColor:buttonColor]; _customCancelButton.titleLabel.font = TGSystemFontOfSize(17.0f); _customCancelButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight; _customCancelButton.hidden = true; [_customCancelButton addTarget:self action:@selector(searchCancelButtonPressed) forControlEvents:UIControlEventTouchUpInside]; [_wrappingView addSubview:_customCancelButton]; } return _customCancelButton; } - (void)setShowsCancelButton:(BOOL)showsCancelButton { [self setShowsCancelButton:showsCancelButton animated:false]; } - (void)setShowsCancelButton:(bool)showsCancelButton animated:(bool)animated { if (_showsCustomCancelButton != showsCancelButton) { if (showsCancelButton) { [self customCancelButton]; _customCancelButton.hidden = _hidesCancelButton; if (_customScopeButtonTitles.count > 1) { self.customScopeButtonContainer.hidden = false; [_customSegmentedControl setSelectedSegmentIndex:0]; } } else { [_customTextField setText:@""]; [self updatePlaceholder:@""]; } _textFieldBackground.image = showsCancelButton ? self.activeTextFieldBackgroundImage : self.normalTextFieldBackgroundImage; _showsCustomCancelButton = showsCancelButton; if (animated) { if (showsCancelButton) _wrappingClip.clipsToBounds = false; [UIView animateWithDuration:0.2 animations:^ { if (!showsCancelButton) { _customTextField.alpha = 0.0f; _customClearButton.alpha = 0.0f; } if (_customScopeButtonTitles.count > 1) { [self setSearchBarShouldShowScopeControl:showsCancelButton]; _customScopeButtonContainer.alpha = showsCancelButton ? 1.0f : 0.0f; } [self layoutSubviews]; _customActiveBackgroundView.alpha = showsCancelButton ? 1.0f : 0.0f; } completion:^(__unused BOOL finished) { //if (finished) { if (showsCancelButton) { _customTextField.alpha = 1.0f; _customClearButton.alpha = 1.0f; } else { _customCancelButton.hidden = true; _customScopeButtonContainer.hidden = true; _wrappingClip.clipsToBounds = true; } } }]; } else { _wrappingClip.clipsToBounds = !showsCancelButton; if (_customScopeButtonTitles.count > 1) { [self setSearchBarShouldShowScopeControl:showsCancelButton]; _customScopeButtonContainer.alpha = showsCancelButton ? 1.0f : 0.0f; } _customTextField.alpha = showsCancelButton ? 1.0f : 0.0f; _customClearButton.alpha = _customTextField.alpha; _customActiveBackgroundView.alpha = showsCancelButton ? 1.0f : 0.0f; _customCancelButton.hidden = !showsCancelButton; _customScopeButtonContainer.hidden = !showsCancelButton; [self layoutSubviews]; } } } - (void)layoutSubviews { [super layoutSubviews]; CGRect bounds = self.bounds; bool landscapeMode = [self landscapeMode]; CGRect clippingFrame = _wrappingClip.frame; clippingFrame.size = CGSizeMake(bounds.size.width, bounds.size.height + 20.0f); _wrappingClip.frame = clippingFrame; CGRect wrappingFrame = _wrappingView.frame; wrappingFrame.size = bounds.size; _wrappingView.frame = wrappingFrame; float retinaPixel = TGIsRetina() ? 0.5f : 0.0f; const float scopeBarHorizontalWidth = 220; CGFloat rightPadding = _showsCustomCancelButton && !_hidesCancelButton ? ((_customScopeButtonContainer != nil && landscapeMode ? scopeBarHorizontalWidth : 0) + _cancelButtonWidth) : 0.0f; _customBackgroundView.frame = CGRectMake(0, ((_showsCustomCancelButton || _alwaysExtended) ? -20.0f : 0.0f), self.frame.size.width, self.frame.size.height + (_showsCustomCancelButton || _alwaysExtended ? 20.0f : 0.0f)); _customActiveBackgroundView.frame = _customBackgroundView.frame; CGSize placeholderSize = [_placeholderLabel.text sizeWithFont:_placeholderLabel.font]; placeholderSize.width = MIN(placeholderSize.width, self.frame.size.width - rightPadding - 40.0f); _textFieldBackground.frame = CGRectMake(8, 9 + [self topPadding], self.frame.size.width - 16 - rightPadding, [self inputHeight]); _customSearchIcon.frame = CGRectMake(_showsCustomCancelButton ? (_textFieldBackground.frame.origin.x + 8.0f) : ((CGFloor((self.frame.size.width - placeholderSize.width) / 2) + 10 + TGRetinaPixel) - 20), [self searchIconOffset] + [self inputContentOffset] + 16 + retinaPixel + [self topPadding], _customSearchIcon.frame.size.width, _customSearchIcon.frame.size.height); _customSearchActivityIndicator.frame = (CGRect){{CGFloor(_customSearchIcon.frame.origin.x + (_customSearchIcon.frame.size.width - _customSearchActivityIndicator.frame.size.width) / 2.0f), CGFloor(_customSearchIcon.frame.origin.y + (_customSearchIcon.frame.size.height - _customSearchActivityIndicator.frame.size.height) / 2.0f) + 1.0f + TGRetinaPixel}, _customSearchActivityIndicator.frame.size}; _placeholderLabel.frame = CGRectMake(_showsCustomCancelButton ? (TGIsRTL() ? (CGRectGetMaxX(_textFieldBackground.frame) - placeholderSize.width - 32.0f) : 36) : (CGFloor((self.frame.size.width - placeholderSize.width) / 2) + 10 + TGRetinaPixel), [self inputContentOffset] + 14 + [self topPadding], placeholderSize.width, placeholderSize.height); if (_customTextField != nil) { CGRect frame = _textFieldBackground.frame; frame.origin.y -= retinaPixel; frame.origin.x += 27; frame.size.width -= 27 + 8 + 24; _customTextField.frame = frame; _customClearButton.frame = CGRectMake(CGRectGetMaxX(_textFieldBackground.frame) - 22, [self inputContentOffset] + 16 + [self topPadding], _customClearButton.frame.size.width, _customClearButton.frame.size.height); } if (_customCancelButton != nil) { _customCancelButton.frame = CGRectMake(self.frame.size.width + (_showsCustomCancelButton ? (-_customCancelButton.frame.size.width - 9) : 9), [self topPadding] + 2.0f, _cancelButtonWidth, [self baseHeight]); } if (_customScopeButtonContainer != nil) { if (_showsCustomCancelButton) { if (!landscapeMode) _customScopeButtonContainer.frame = CGRectMake(7.0f, self.frame.size.height - 29.0f - 9.0f + [self topPadding], self.frame.size.width - 14.0f, 29.0f); else _customScopeButtonContainer.frame = CGRectMake(self.frame.size.width - scopeBarHorizontalWidth - _customCancelButton.frame.size.width, 5.0f + [self topPadding], scopeBarHorizontalWidth - 14.0f, 32.0f); } else { if (!landscapeMode) _customScopeButtonContainer.frame = CGRectMake(7.0f, self.frame.size.height - 29.0f - 9.0f + [self topPadding], self.frame.size.width - 14.0f, 29.0f); else _customScopeButtonContainer.frame = CGRectMake(self.frame.size.width + 71.0f, 5.0f + [self topPadding], scopeBarHorizontalWidth - 14.0f, 29.0f); } } } - (bool)landscapeMode { static CGFloat landscapeScreenWidth = 0.0f; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { CGSize screenSize = TGScreenSize(); landscapeScreenWidth = MAX(screenSize.width, screenSize.height); }); return self.frame.size.width >= landscapeScreenWidth - FLT_EPSILON; } - (void)setSearchBarShouldShowScopeControl:(bool)searchBarShouldShowScopeControl { _searchBarShouldShowScopeControl = searchBarShouldShowScopeControl; if (_searchBarShouldShowScopeControl) self.customScopeButtonContainer.hidden = false; CGFloat requiredHeight = 0; if (_searchBarShouldShowScopeControl && ![self landscapeMode]) requiredHeight = [self baseHeight] + [TGSearchBar searchBarScopeHeight]; else requiredHeight = [self baseHeight]; if (ABS(requiredHeight - self.frame.size.height) > FLT_EPSILON) { id delegate = (id)self.delegate; if ([delegate respondsToSelector:@selector(searchBar:willChangeHeight:)]) [delegate searchBar:self willChangeHeight:requiredHeight]; } } - (void)setCustomScopeBarHidden:(bool)hidden { self.customScopeButtonContainer.alpha = hidden ? 0.0f : 1.0f; self.customScopeButtonContainer.userInteractionEnabled = !hidden; } #pragma mark - - (void)tappedSearchBar:(id)__unused arg { } - (BOOL)becomeFirstResponder { if (![_customTextField isFirstResponder]) { bool shouldBeginEditing = true; id delegate = self.delegate; if ([delegate respondsToSelector:@selector(searchBarShouldBeginEditing:)]) shouldBeginEditing = [delegate searchBarShouldBeginEditing:(UISearchBar *)self]; if (shouldBeginEditing) { [self.customTextField becomeFirstResponder]; if ([delegate respondsToSelector:@selector(searchBarTextDidBeginEditing:)]) [delegate searchBarTextDidBeginEditing:(UISearchBar *)self]; return true; } } return false; } - (BOOL)resignFirstResponder { return [_customTextField resignFirstResponder]; } - (BOOL)canBecomeFirstResponder { return _customTextField == nil || [_customTextField canBecomeFirstResponder]; } - (BOOL)canResignFirstResponder { return [_customTextField canResignFirstResponder]; } #pragma mark - - (void)searchCancelButtonPressed { id delegate = self.delegate; if ([delegate respondsToSelector:@selector(searchBarCancelButtonClicked:)]) [delegate searchBarCancelButtonClicked:(UISearchBar *)self]; } - (UIView *)customScopeButtonContainer { if (_customScopeButtonContainer == nil) { CGRect frame = CGRectZero; if (![self landscapeMode]) frame = CGRectMake(7.0f, self.frame.size.height - 29.0f - 9.0f, self.frame.size.width - 14.0f, 29.0f); else frame = CGRectMake(0, 0, self.frame.size.width, 29.0f); _customScopeButtonContainer = [[UIView alloc] initWithFrame:frame]; _customScopeButtonContainer.alpha = 0.0f; [_wrappingView insertSubview:_customScopeButtonContainer aboveSubview:_customActiveBackgroundView]; _customSegmentedControl = [[UISegmentedControl alloc] initWithItems:self.customScopeButtonTitles]; [_customSegmentedControl setBackgroundImage:[UIImage imageNamed:@"ModernSegmentedControlBackground.png"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; [_customSegmentedControl setBackgroundImage:[UIImage imageNamed:@"ModernSegmentedControlSelected.png"] forState:UIControlStateSelected barMetrics:UIBarMetricsDefault]; [_customSegmentedControl setBackgroundImage:[UIImage imageNamed:@"ModernSegmentedControlSelected.png"] forState:UIControlStateSelected | UIControlStateHighlighted barMetrics:UIBarMetricsDefault]; [_customSegmentedControl setBackgroundImage:[UIImage imageNamed:@"ModernSegmentedControlHighlighted.png"] forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault]; UIImage *dividerImage = [UIImage imageNamed:@"ModernSegmentedControlDivider.png"]; [_customSegmentedControl setDividerImage:dividerImage forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault]; [_customSegmentedControl setTitleTextAttributes:@{UITextAttributeTextColor: TGAccentColor(), UITextAttributeTextShadowColor: [UIColor clearColor], UITextAttributeFont: TGSystemFontOfSize(13)} forState:UIControlStateNormal]; [_customSegmentedControl setTitleTextAttributes:@{UITextAttributeTextColor: [UIColor whiteColor], UITextAttributeTextShadowColor: [UIColor clearColor], UITextAttributeFont: TGSystemFontOfSize(13)} forState:UIControlStateSelected]; _customSegmentedControl.frame = CGRectMake(0, _customScopeButtonContainer.frame.size.height - 29.0f, _customScopeButtonContainer.frame.size.width, 29.0f); _customSegmentedControl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; [_customSegmentedControl setSelectedSegmentIndex:0]; [_customSegmentedControl addTarget:self action:@selector(segmentedControlChanged) forControlEvents:UIControlEventValueChanged]; [_customScopeButtonContainer addSubview:_customSegmentedControl]; } return _customScopeButtonContainer; } - (void)backgroundTapGesture:(UITapGestureRecognizer *)recognizer { if (recognizer.state == UIGestureRecognizerStateRecognized) { if (![_customTextField isFirstResponder]) { bool shouldBeginEditing = true; id delegate = self.delegate; if ([delegate respondsToSelector:@selector(searchBarShouldBeginEditing:)]) shouldBeginEditing = [delegate searchBarShouldBeginEditing:(UISearchBar *)self]; if (shouldBeginEditing) { [self.customTextField becomeFirstResponder]; if ([delegate respondsToSelector:@selector(searchBarTextDidBeginEditing:)]) [delegate searchBarTextDidBeginEditing:(UISearchBar *)self]; } } } } - (void)updatePlaceholder:(NSString *)text { _placeholderLabel.hidden = text.length != 0; _customClearButton.hidden = !_placeholderLabel.hidden; } - (void)textFieldDidChange:(UITextField *)textField { if (textField == _customTextField) { NSString *text = textField.text; id delegate = self.delegate; if ([delegate respondsToSelector:@selector(searchBar:textDidChange:)]) [delegate searchBar:(UISearchBar *)self textDidChange:text]; [self updatePlaceholder:text]; } } - (BOOL)textFieldShouldReturn:(UITextField *)textField { if (textField == _customTextField) { if (textField.text.length != 0) { id delegate = self.delegate; if ([delegate respondsToSelector:@selector(searchBarSearchButtonClicked:)]) [delegate searchBarSearchButtonClicked:(UISearchBar *)self]; } [textField resignFirstResponder]; return false; } return false; } - (void)customClearButtonPressed { [_customTextField setText:@""]; [self updatePlaceholder:@""]; [self becomeFirstResponder]; NSString *text = @""; id delegate = self.delegate; if ([delegate respondsToSelector:@selector(searchBar:textDidChange:)]) [delegate searchBar:(UISearchBar *)self textDidChange:text]; } - (NSInteger)selectedScopeButtonIndex { return _customSegmentedControl.selectedSegmentIndex; } - (void)setSelectedScopeButtonIndex:(NSInteger)selectedScopeButtonIndex { [self.customSegmentedControl setSelectedSegmentIndex:selectedScopeButtonIndex]; } - (void)setPlaceholder:(NSString *)placeholder { _placeholder = placeholder; _placeholderLabel.text = placeholder; [self setNeedsLayout]; } - (NSString *)text { return _customTextField.text; } - (void)setText:(NSString *)text { bool layout = _customTextField == nil; self.customTextField.text = text; if (layout) [self setNeedsLayout]; [self textFieldDidChange:_customTextField]; } - (void)segmentedControlChanged { if (_showsCustomCancelButton) { id delegate = self.delegate; if ([delegate respondsToSelector:@selector(searchBar:selectedScopeButtonIndexDidChange:)]) [delegate searchBar:(UISearchBar *)self selectedScopeButtonIndexDidChange:_customSegmentedControl.selectedSegmentIndex]; } } - (void)updateClipping:(CGFloat)clippedHeight { CGFloat offset = self.frame.size.height + MAX(0.0f, MIN(clippedHeight, self.frame.size.height)); CGRect frame = _wrappingClip.frame; frame.origin.y = offset - frame.size.height + 20.0f; _wrappingClip.frame = frame; CGRect wrapFrame = _wrappingView.frame; wrapFrame.origin.y = -offset + wrapFrame.size.height; _wrappingView.frame = wrapFrame; } - (void)localizationUpdated { _placeholderLabel.text = TGLocalized(@"Common.Search"); _cancelButtonWidth = [TGLocalized(@"Common.Cancel") sizeWithFont:TGSystemFontOfSize(17.0f)].width + 11.0f; CGRect textFieldBackgroundFrame = _textFieldBackground.frame; _customCancelButton.frame = CGRectMake(textFieldBackgroundFrame.origin.x + textFieldBackgroundFrame.size.width + 10, 0, _cancelButtonWidth, [self baseHeight]); [_customCancelButton setTitle:TGLocalized(@"Common.Cancel") forState:UIControlStateNormal]; [_customSegmentedControl removeAllSegments]; int index = -1; for (NSString *itemText in _customScopeButtonTitles) { index++; [_customSegmentedControl insertSegmentWithTitle:itemText atIndex:(NSUInteger)index animated:false]; } [self setNeedsLayout]; } - (void)setShowActivity:(bool)showActivity { if (_showActivity != showActivity) { [_searchActivityTimer invalidate]; _searchActivityTimer = nil; _showActivity = showActivity; if (_delayActivity && showActivity) { _searchActivityTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(searchActivityTimerEvent) interval:0.2 repeat:false]; } else [self searchActivityTimerEvent]; } } - (void)searchActivityTimerEvent { _customSearchIcon.hidden = _showActivity; if (_showActivity) { _customSearchActivityIndicator.hidden = false; [_customSearchActivityIndicator startAnimating]; } else { _customSearchActivityIndicator.hidden = true; [_customSearchActivityIndicator stopAnimating]; } } @end