// // IQTextView.m // https://github.com/hackiftekhar/IQKeyboardManager // Copyright (c) 2013-24 Iftekhar Qurashi. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #import #import "IQTextView.h" NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension") @interface IQTextView () @property(nullable, nonatomic, strong) UILabel *placeholderLabel; @end NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension") @implementation IQTextView @synthesize placeholder = _placeholder; @synthesize placeholderLabel = _placeholderLabel; @synthesize placeholderTextColor = _placeholderTextColor; -(void)initialize { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshPlaceholder) name:UITextViewTextDidChangeNotification object:self]; } -(void)dealloc { [_placeholderLabel removeFromSuperview]; _placeholderLabel = nil; [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (instancetype)init { self = [super init]; if (self) { [self initialize]; } return self; } -(void)awakeFromNib { [super awakeFromNib]; [self initialize]; } -(void)refreshPlaceholder { if([[self text] length] || [[self attributedText] length]) { if (self.placeholderLabel.alpha != 0) { [self.placeholderLabel setAlpha:0]; [self setNeedsLayout]; [self layoutIfNeeded]; } } else if(self.placeholderLabel.alpha != 1) { [self.placeholderLabel setAlpha:1]; [self setNeedsLayout]; [self layoutIfNeeded]; } } - (void)setText:(NSString *)text { [super setText:text]; [self refreshPlaceholder]; } -(void)setAttributedText:(NSAttributedString *)attributedText { [super setAttributedText:attributedText]; [self refreshPlaceholder]; } -(void)setFont:(UIFont *)font { [super setFont:font]; self.placeholderLabel.font = self.font; [self setNeedsLayout]; [self layoutIfNeeded]; } -(void)setTextAlignment:(NSTextAlignment)textAlignment { [super setTextAlignment:textAlignment]; self.placeholderLabel.textAlignment = textAlignment; [self setNeedsLayout]; [self layoutIfNeeded]; } -(void)layoutSubviews { [super layoutSubviews]; self.placeholderLabel.frame = [self placeholderExpectedFrame]; } -(void)setPlaceholder:(NSString *)placeholder { _placeholder = placeholder; self.placeholderLabel.text = placeholder; [self refreshPlaceholder]; } -(void)setAttributedPlaceholder:(NSAttributedString *)attributedPlaceholder { _attributedPlaceholder = attributedPlaceholder; self.placeholderLabel.attributedText = attributedPlaceholder; [self refreshPlaceholder]; } -(void)setPlaceholderTextColor:(UIColor*)placeholderTextColor { _placeholderTextColor = placeholderTextColor; self.placeholderLabel.textColor = placeholderTextColor; } -(UIEdgeInsets)placeholderInsets { return UIEdgeInsetsMake(self.textContainerInset.top, self.textContainerInset.left + self.textContainer.lineFragmentPadding, self.textContainerInset.bottom, self.textContainerInset.right + self.textContainer.lineFragmentPadding); } -(CGRect)placeholderExpectedFrame { UIEdgeInsets placeholderInsets = [self placeholderInsets]; CGFloat maxWidth = CGRectGetWidth(self.frame)-placeholderInsets.left-placeholderInsets.right; CGSize expectedSize = [self.placeholderLabel sizeThatFits:CGSizeMake(maxWidth, CGRectGetHeight(self.frame)-placeholderInsets.top-placeholderInsets.bottom)]; return CGRectMake(placeholderInsets.left, placeholderInsets.top, maxWidth, expectedSize.height); } -(UILabel*)placeholderLabel { if (_placeholderLabel == nil) { _placeholderLabel = [[UILabel alloc] init]; _placeholderLabel.autoresizingMask = (UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight); _placeholderLabel.lineBreakMode = NSLineBreakByWordWrapping; _placeholderLabel.numberOfLines = 0; _placeholderLabel.font = self.font; _placeholderLabel.textAlignment = self.textAlignment; _placeholderLabel.backgroundColor = [UIColor clearColor]; _placeholderLabel.isAccessibilityElement = NO; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 if (@available(iOS 13.0, *)) { _placeholderLabel.textColor = [UIColor placeholderTextColor]; } else #endif { _placeholderLabel.textColor = [UIColor lightTextColor]; } _placeholderLabel.alpha = 0; [self addSubview:_placeholderLabel]; } return _placeholderLabel; } //When any text changes on textField, the delegate getter is called. At this time we refresh the textView's placeholder -(id)delegate { [self refreshPlaceholder]; return [super delegate]; } -(CGSize)intrinsicContentSize { if (self.hasText) { return [super intrinsicContentSize]; } UIEdgeInsets placeholderInsets = [self placeholderInsets]; CGSize newSize = [super intrinsicContentSize]; newSize.height = [self placeholderExpectedFrame].size.height + placeholderInsets.top + placeholderInsets.bottom; return newSize; } - (CGRect)caretRectForPosition:(UITextPosition *)position { CGRect originalRect = [super caretRectForPosition:position]; // When placeholder is visible and text alignment is centered if (_placeholderLabel.alpha == 1 && self.textAlignment == NSTextAlignmentCenter) { // Calculate the width of the placeholder text CGSize textSize = [_placeholderLabel.text sizeWithAttributes:@{NSFontAttributeName:_placeholderLabel.font}]; // Calculate the starting x position of the centered placeholder text CGFloat centeredTextX = (self.bounds.size.width - textSize.width) / 2; // Update the caret position to match the starting x position of the centered text originalRect.origin.x = centeredTextX; } return originalRect; } @end