diff options
Diffstat (limited to 'modules/react-native-ui-text-view/ios')
7 files changed, 0 insertions, 424 deletions
diff --git a/modules/react-native-ui-text-view/ios/RNUITextView-Bridging-Header.h b/modules/react-native-ui-text-view/ios/RNUITextView-Bridging-Header.h deleted file mode 100644 index e669b47eb..000000000 --- a/modules/react-native-ui-text-view/ios/RNUITextView-Bridging-Header.h +++ /dev/null @@ -1,3 +0,0 @@ -#import <React/RCTViewManager.h> -#import <React/RCTBridge.h> -#import <React/RCTBridge+Private.h> diff --git a/modules/react-native-ui-text-view/ios/RNUITextView.swift b/modules/react-native-ui-text-view/ios/RNUITextView.swift deleted file mode 100644 index 3fb55873d..000000000 --- a/modules/react-native-ui-text-view/ios/RNUITextView.swift +++ /dev/null @@ -1,153 +0,0 @@ -class RNUITextView: UIView { - var textView: UITextView - - @objc var numberOfLines: Int = 0 { - didSet { - textView.textContainer.maximumNumberOfLines = numberOfLines - } - } - @objc var selectable: Bool = true { - didSet { - textView.isSelectable = selectable - } - } - @objc var ellipsizeMode: String = "tail" { - didSet { - textView.textContainer.lineBreakMode = self.getLineBreakMode() - } - } - @objc var onTextLayout: RCTDirectEventBlock? - - override init(frame: CGRect) { - if #available(iOS 16.0, *) { - textView = UITextView(usingTextLayoutManager: false) - } else { - textView = UITextView() - } - - // Disable scrolling - textView.isScrollEnabled = false - // Remove all the padding - textView.textContainerInset = .zero - textView.textContainer.lineFragmentPadding = 0 - - // Remove other properties - textView.isEditable = false - textView.backgroundColor = .clear - - // Init - super.init(frame: frame) - self.clipsToBounds = true - - // Add the view - addSubview(textView) - - let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(callOnPress(_:))) - tapGestureRecognizer.isEnabled = true - textView.addGestureRecognizer(tapGestureRecognizer) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // Resolves some animation issues - override func reactSetFrame(_ frame: CGRect) { - UIView.performWithoutAnimation { - super.reactSetFrame(frame) - } - } - - func setText(string: NSAttributedString, size: CGSize, numberOfLines: Int) -> Void { - self.textView.frame.size = size - self.textView.textContainer.maximumNumberOfLines = numberOfLines - self.textView.attributedText = string - self.textView.selectedTextRange = nil - - if let onTextLayout = self.onTextLayout { - var lines: [String] = [] - textView.layoutManager.enumerateLineFragments( - forGlyphRange: NSRange(location: 0, length: textView.attributedText.length)) - { (rect, usedRect, textContainer, glyphRange, stop) in - let characterRange = self.textView.layoutManager.characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil) - let line = (self.textView.text as NSString).substring(with: characterRange) - lines.append(line) - } - - onTextLayout([ - "lines": lines - ]) - } - } - - @IBAction func callOnPress(_ sender: UITapGestureRecognizer) -> Void { - // If we find a child, then call onPress - if let child = getPressed(sender) { - if textView.selectedTextRange == nil, let onPress = child.onPress { - onPress(["": ""]) - } else { - // Clear the selected text range if we are not pressing on a link - textView.selectedTextRange = nil - } - } - } - - // Try to get the pressed segment - func getPressed(_ sender: UITapGestureRecognizer) -> RNUITextViewChild? { - let layoutManager = textView.layoutManager - var location = sender.location(in: textView) - - // Remove the padding - location.x -= textView.textContainerInset.left - location.y -= textView.textContainerInset.top - - // Get the index of the char - let charIndex = layoutManager.characterIndex( - for: location, - in: textView.textContainer, - fractionOfDistanceBetweenInsertionPoints: nil - ) - - var lastUpperBound: String.Index? = nil - for child in self.reactSubviews() { - if let child = child as? RNUITextViewChild, let childText = child.text { - let fullText = self.textView.attributedText.string - - // We want to skip over the children we have already checked, otherwise we could run into - // collisions of similar strings (i.e. links that get shortened to the same hostname but - // different paths) - let range = fullText.range(of: childText, options: [], range: (lastUpperBound ?? String.Index(utf16Offset: 0, in: fullText) )..<fullText.endIndex) - - if let lowerBound = range?.lowerBound, let upperBound = range?.upperBound { - let lowerOffset = lowerBound.utf16Offset(in: fullText) - let upperOffset = upperBound.utf16Offset(in: fullText) - - if charIndex >= lowerOffset, - charIndex <= upperOffset - { - return child - } else { - lastUpperBound = upperBound - } - } - } - } - - return nil - } - - func getLineBreakMode() -> NSLineBreakMode { - switch self.ellipsizeMode { - case "head": - return .byTruncatingHead - case "middle": - return .byTruncatingMiddle - case "tail": - return .byTruncatingTail - case "clip": - return .byClipping - default: - return .byTruncatingTail - } - } -} diff --git a/modules/react-native-ui-text-view/ios/RNUITextViewChild.swift b/modules/react-native-ui-text-view/ios/RNUITextViewChild.swift deleted file mode 100644 index c341c46e4..000000000 --- a/modules/react-native-ui-text-view/ios/RNUITextViewChild.swift +++ /dev/null @@ -1,4 +0,0 @@ -class RNUITextViewChild: UIView { - @objc var text: String? - @objc var onPress: RCTDirectEventBlock? -} diff --git a/modules/react-native-ui-text-view/ios/RNUITextViewChildShadow.swift b/modules/react-native-ui-text-view/ios/RNUITextViewChildShadow.swift deleted file mode 100644 index 09119a369..000000000 --- a/modules/react-native-ui-text-view/ios/RNUITextViewChildShadow.swift +++ /dev/null @@ -1,56 +0,0 @@ -// We want all of our props to be available in the child's shadow view so we -// can create the attributed text before mount and calculate the needed size -// for the view. -class RNUITextViewChildShadow: RCTShadowView { - @objc var text: String = "" - @objc var color: UIColor = .black - @objc var fontSize: CGFloat = 16.0 - @objc var fontStyle: String = "normal" - @objc var fontWeight: String = "normal" - @objc var letterSpacing: CGFloat = 0.0 - @objc var lineHeight: CGFloat = 0.0 - @objc var pointerEvents: NSString? - - override func isYogaLeafNode() -> Bool { - return true - } - - override func didSetProps(_ changedProps: [String]!) { - guard let superview = self.superview as? RNUITextViewShadow else { - return - } - - if !YGNodeIsDirty(superview.yogaNode) { - superview.setAttributedText() - } - } - - func getFontWeight() -> UIFont.Weight { - switch self.fontWeight { - case "bold": - return .bold - case "normal": - return .regular - case "100": - return .ultraLight - case "200": - return .ultraLight - case "300": - return .light - case "400": - return .regular - case "500": - return .medium - case "600": - return .semibold - case "700": - return .semibold - case "800": - return .bold - case "900": - return .heavy - default: - return .regular - } - } -} diff --git a/modules/react-native-ui-text-view/ios/RNUITextViewManager.m b/modules/react-native-ui-text-view/ios/RNUITextViewManager.m deleted file mode 100644 index 32dfb3b28..000000000 --- a/modules/react-native-ui-text-view/ios/RNUITextViewManager.m +++ /dev/null @@ -1,26 +0,0 @@ -#import <React/RCTViewManager.h> - -@interface RCT_EXTERN_MODULE(RNUITextViewManager, RCTViewManager) -RCT_REMAP_SHADOW_PROPERTY(numberOfLines, numberOfLines, NSInteger) -RCT_REMAP_SHADOW_PROPERTY(allowsFontScaling, allowsFontScaling, BOOL) - -RCT_EXPORT_VIEW_PROPERTY(numberOfLines, NSInteger) -RCT_EXPORT_VIEW_PROPERTY(onTextLayout, RCTDirectEventBlock) -RCT_EXPORT_VIEW_PROPERTY(ellipsizeMode, NSString) -RCT_EXPORT_VIEW_PROPERTY(selectable, BOOL) - -@end - -@interface RCT_EXTERN_MODULE(RNUITextViewChildManager, RCTViewManager) -RCT_REMAP_SHADOW_PROPERTY(text, text, NSString) -RCT_REMAP_SHADOW_PROPERTY(color, color, UIColor) -RCT_REMAP_SHADOW_PROPERTY(fontSize, fontSize, CGFloat) -RCT_REMAP_SHADOW_PROPERTY(fontStyle, fontStyle, NSString) -RCT_REMAP_SHADOW_PROPERTY(fontWeight, fontWeight, NSString) -RCT_REMAP_SHADOW_PROPERTY(letterSpacing, letterSpacing, CGFloat) -RCT_REMAP_SHADOW_PROPERTY(lineHeight, lineHeight, CGFloat) -RCT_REMAP_SHADOW_PROPERTY(pointerEvents, pointerEvents, NSString) - -RCT_EXPORT_VIEW_PROPERTY(text, NSString) -RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock) -@end diff --git a/modules/react-native-ui-text-view/ios/RNUITextViewManager.swift b/modules/react-native-ui-text-view/ios/RNUITextViewManager.swift deleted file mode 100644 index 297bcbbb2..000000000 --- a/modules/react-native-ui-text-view/ios/RNUITextViewManager.swift +++ /dev/null @@ -1,30 +0,0 @@ -@objc(RNUITextViewManager) -class RNUITextViewManager: RCTViewManager { - override func view() -> (RNUITextView) { - return RNUITextView() - } - - @objc override static func requiresMainQueueSetup() -> Bool { - return true - } - - override func shadowView() -> RCTShadowView { - // Pass the bridge to the shadow view - return RNUITextViewShadow(bridge: self.bridge) - } -} - -@objc(RNUITextViewChildManager) -class RNUITextViewChildManager: RCTViewManager { - override func view() -> (RNUITextViewChild) { - return RNUITextViewChild() - } - - @objc override static func requiresMainQueueSetup() -> Bool { - return true - } - - override func shadowView() -> RCTShadowView { - return RNUITextViewChildShadow() - } -} diff --git a/modules/react-native-ui-text-view/ios/RNUITextViewShadow.swift b/modules/react-native-ui-text-view/ios/RNUITextViewShadow.swift deleted file mode 100644 index 5a462f6b6..000000000 --- a/modules/react-native-ui-text-view/ios/RNUITextViewShadow.swift +++ /dev/null @@ -1,152 +0,0 @@ -class RNUITextViewShadow: RCTShadowView { - // Props - @objc var numberOfLines: Int = 0 { - didSet { - if !YGNodeIsDirty(self.yogaNode) { - self.setAttributedText() - } - } - } - @objc var allowsFontScaling: Bool = true - - var attributedText: NSAttributedString = NSAttributedString() - var frameSize: CGSize = CGSize() - - var lineHeight: CGFloat = 0 - - var bridge: RCTBridge - - init(bridge: RCTBridge) { - self.bridge = bridge - super.init() - - // We need to set a custom measure func here to calculate the height correctly - YGNodeSetMeasureFunc(self.yogaNode) { node, width, widthMode, height, heightMode in - // Get the shadowview and determine the needed size to set - let shadowView = Unmanaged<RNUITextViewShadow>.fromOpaque(YGNodeGetContext(node)).takeUnretainedValue() - return shadowView.getNeededSize(maxWidth: width) - } - - // Subscribe to ynamic type size changes - NotificationCenter.default.addObserver( - self, - selector: #selector(preferredContentSizeChanged(_:)), - name: UIContentSizeCategory.didChangeNotification, - object: nil - ) - } - - @objc func preferredContentSizeChanged(_ notification: Notification) { - self.setAttributedText() - } - - // Returning true here will tell Yoga to not use flexbox and instead use our custom measure func. - override func isYogaLeafNode() -> Bool { - return true - } - - // We should only insert children that are UITextView shadows - override func insertReactSubview(_ subview: RCTShadowView!, at atIndex: Int) { - if subview.isKind(of: RNUITextViewChildShadow.self) { - super.insertReactSubview(subview, at: atIndex) - } - } - - // Every time the subviews change, we need to reformat and render the text. - override func didUpdateReactSubviews() { - self.setAttributedText() - } - - // Whenever we layout, update the UI - override func layoutSubviews(with layoutContext: RCTLayoutContext) { - // Don't do anything if the layout is dirty - if(YGNodeIsDirty(self.yogaNode)) { - return - } - - // Since we are inside the shadow view here, we have to find the real view and update the text. - self.bridge.uiManager.addUIBlock { uiManager, viewRegistry in - guard let textView = viewRegistry?[self.reactTag] as? RNUITextView else { - return - } - textView.setText(string: self.attributedText, size: self.frameSize, numberOfLines: self.numberOfLines) - } - } - - override func dirtyLayout() { - super.dirtyLayout() - YGNodeMarkDirty(self.yogaNode) - } - - // Update the attributed text whenever changes are made to the subviews. - func setAttributedText() -> Void { - // Create an attributed string to store each of the segments - let finalAttributedString = NSMutableAttributedString() - - self.reactSubviews().forEach { child in - guard let child = child as? RNUITextViewChildShadow else { - return - } - let scaledFontSize = self.allowsFontScaling ? - UIFontMetrics.default.scaledValue(for: child.fontSize) : child.fontSize - let font = UIFont.systemFont(ofSize: scaledFontSize, weight: child.getFontWeight()) - - // Set some generic attributes that don't need ranges - let attributes: [NSAttributedString.Key:Any] = [ - .font: font, - .foregroundColor: child.color, - ] - - // Create the attributed string with the generic attributes - let string = NSMutableAttributedString(string: child.text, attributes: attributes) - - // Set the paragraph style attributes if necessary. We can check this by seeing if the provided - // line height is not 0.0. - let paragraphStyle = NSMutableParagraphStyle() - if child.lineHeight != 0.0 { - // Whenever we change the line height for the text, we are also removing the DynamicType - // adjustment for line height. We need to get the multiplier and apply that to the - // line height. - let scaleMultiplier = scaledFontSize / child.fontSize - paragraphStyle.minimumLineHeight = child.lineHeight * scaleMultiplier - paragraphStyle.maximumLineHeight = child.lineHeight * scaleMultiplier - - string.addAttribute( - NSAttributedString.Key.paragraphStyle, - value: paragraphStyle, - range: NSMakeRange(0, string.length) - ) - - // To calcualte the size of the text without creating a new UILabel or UITextView, we have - // to store this line height for later. - self.lineHeight = child.lineHeight - } else { - self.lineHeight = font.lineHeight - } - - finalAttributedString.append(string) - } - - self.attributedText = finalAttributedString - self.dirtyLayout() - } - - // To create the needed size we need to: - // 1. Get the max size that we can use for the view - // 2. Calculate the height of the text based on that max size - // 3. Determine how many lines the text is, and limit that number if it exceeds the max - // 4. Set the frame size and return the YGSize. YGSize requires Float values while CGSize needs CGFloat - func getNeededSize(maxWidth: Float) -> YGSize { - let maxSize = CGSize(width: CGFloat(maxWidth), height: CGFloat(MAXFLOAT)) - let textSize = self.attributedText.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, context: nil) - - var totalLines = Int(ceil(textSize.height / self.lineHeight)) - - if self.numberOfLines != 0, totalLines > self.numberOfLines { - totalLines = self.numberOfLines - } - - self.frameSize = CGSize(width: CGFloat(maxWidth), height: CGFloat(CGFloat(totalLines) * self.lineHeight)) - return YGSize(width: Float(self.frameSize.width), height: Float(self.frameSize.height)) - } -} |