You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
6.1 KiB

5 years ago
  1. //
  2. // SlackTextViewController
  3. // https://github.com/slackhq/SlackTextViewController
  4. //
  5. // Copyright 2014-2016 Slack Technologies, Inc.
  6. // Licence: MIT-Licence
  7. //
  8. #import "SLKTextView+SLKAdditions.h"
  9. @implementation SLKTextView (SLKAdditions)
  10. - (void)slk_clearText:(BOOL)clearUndo
  11. {
  12. // Important to call self implementation, as SLKTextView overrides setText: to add additional features.
  13. [self setAttributedText:nil];
  14. if (self.undoManagerEnabled && clearUndo) {
  15. [self.undoManager removeAllActions];
  16. }
  17. }
  18. - (void)slk_scrollToCaretPositonAnimated:(BOOL)animated
  19. {
  20. if (animated) {
  21. [self scrollRangeToVisible:self.selectedRange];
  22. }
  23. else {
  24. [UIView performWithoutAnimation:^{
  25. [self scrollRangeToVisible:self.selectedRange];
  26. }];
  27. }
  28. }
  29. - (void)slk_scrollToBottomAnimated:(BOOL)animated
  30. {
  31. CGRect rect = [self caretRectForPosition:self.selectedTextRange.end];
  32. rect.size.height += self.textContainerInset.bottom;
  33. if (animated) {
  34. [self scrollRectToVisible:rect animated:animated];
  35. }
  36. else {
  37. [UIView performWithoutAnimation:^{
  38. [self scrollRectToVisible:rect animated:NO];
  39. }];
  40. }
  41. }
  42. - (void)slk_insertNewLineBreak
  43. {
  44. [self slk_insertTextAtCaretRange:@"\n"];
  45. // if the text view cannot expand anymore, scrolling to bottom are not animated to fix a UITextView issue scrolling twice.
  46. BOOL animated = !self.isExpanding;
  47. //Detected break. Should scroll to bottom if needed.
  48. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.0125 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  49. [self slk_scrollToBottomAnimated:animated];
  50. });
  51. }
  52. - (void)slk_insertTextAtCaretRange:(NSString *)text
  53. {
  54. NSRange range = [self slk_insertText:text inRange:self.selectedRange];
  55. self.selectedRange = NSMakeRange(range.location, 0);
  56. }
  57. - (void)slk_insertTextAtCaretRange:(NSString *)text withAttributes:(NSDictionary<NSString *, id> *)attributes
  58. {
  59. NSRange range = [self slk_insertText:text withAttributes:attributes inRange:self.selectedRange];
  60. self.selectedRange = NSMakeRange(range.location, 0);
  61. }
  62. - (NSRange)slk_insertText:(NSString *)text inRange:(NSRange)range
  63. {
  64. NSAttributedString *attributedText = [self slk_defaultAttributedStringForText:text];
  65. return [self slk_insertAttributedText:attributedText inRange:range];
  66. }
  67. - (NSRange)slk_insertText:(NSString *)text withAttributes:(NSDictionary<NSString *, id> *)attributes inRange:(NSRange)range
  68. {
  69. NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:attributes];
  70. return [self slk_insertAttributedText:attributedText inRange:range];
  71. }
  72. - (NSAttributedString *)slk_setAttributes:(NSDictionary<NSString *, id> *)attributes
  73. inRange:(NSRange)range
  74. {
  75. NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];
  76. [attributedText setAttributes:attributes range:range];
  77. [self setAttributedText:attributedText];
  78. return self.attributedText;
  79. }
  80. - (void)slk_insertAttributedTextAtCaretRange:(NSAttributedString *)attributedText
  81. {
  82. NSRange range = [self slk_insertAttributedText:attributedText inRange:self.selectedRange];
  83. self.selectedRange = NSMakeRange(range.location, 0);
  84. }
  85. - (NSRange)slk_insertAttributedText:(NSAttributedString *)attributedText inRange:(NSRange)range
  86. {
  87. // Skip if the attributed text is empty
  88. if (attributedText.length == 0) {
  89. return NSMakeRange(0, 0);
  90. }
  91. // Registers for undo management
  92. [self slk_prepareForUndo:@"Attributed text appending"];
  93. // Append the new string at the caret position
  94. if (range.length == 0)
  95. {
  96. NSAttributedString *leftAttributedString = [self.attributedText attributedSubstringFromRange:NSMakeRange(0, range.location)];
  97. NSAttributedString *rightAttributedString = [self.attributedText attributedSubstringFromRange:NSMakeRange(range.location, self.attributedText.length-range.location)];
  98. NSMutableAttributedString *newAttributedText = [NSMutableAttributedString new];
  99. [newAttributedText appendAttributedString:leftAttributedString];
  100. [newAttributedText appendAttributedString:attributedText];
  101. [newAttributedText appendAttributedString:rightAttributedString];
  102. [self setAttributedText:newAttributedText];
  103. range.location += attributedText.length;
  104. return range;
  105. }
  106. // Some text is selected, so we replace it with the new text
  107. else if (range.location != NSNotFound && range.length > 0)
  108. {
  109. NSMutableAttributedString *mutableAttributeText = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];
  110. [mutableAttributeText replaceCharactersInRange:range withAttributedString:attributedText];
  111. [self setAttributedText:mutableAttributeText];
  112. range.location += self.attributedText.length;
  113. return range;
  114. }
  115. // No text has been inserted, but still return the caret range
  116. return self.selectedRange;
  117. }
  118. - (void)slk_clearAllAttributesInRange:(NSRange)range
  119. {
  120. NSMutableAttributedString *mutableAttributedText = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];
  121. [mutableAttributedText setAttributes:nil range:range];
  122. [self setAttributedText:mutableAttributedText];
  123. }
  124. - (NSAttributedString *)slk_defaultAttributedStringForText:(NSString *)text
  125. {
  126. NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
  127. if (self.textColor) {
  128. attributes[NSForegroundColorAttributeName] = self.textColor;
  129. }
  130. if (self.font) {
  131. attributes[NSFontAttributeName] = self.font;
  132. }
  133. return [[NSAttributedString alloc] initWithString:text attributes:attributes];
  134. }
  135. - (void)slk_prepareForUndo:(NSString *)description
  136. {
  137. if (!self.undoManagerEnabled) {
  138. return;
  139. }
  140. SLKTextView *prepareInvocation = [self.undoManager prepareWithInvocationTarget:self];
  141. [prepareInvocation setText:self.text];
  142. [self.undoManager setActionName:description];
  143. }
  144. @end