【问题标题】:Copy HTML to UIPasteboard with React Native使用 React Native 将 HTML 复制到 UIPasteboard
【发布时间】:2020-12-15 14:46:53
【问题描述】:

我有一个简单的 react-native 应用程序,它具有一些将 HTML 复制到全局剪贴板的功能,以便保留样式。预期的结果是我复制 HTML 并可以将其粘贴到另一个应用程序中,并且样式保持不变。请注意,我不是要粘贴 HTML 源代码,这很简单,只需复制为字符串即可。

对于 iOS 13,我对 react-native clipboard native module 进行了小修改,将其从复制纯文本更改为复制 HTML。此代码在 iOS 13 上按预期工作,但自从更新到 14 后它似乎不起作用 - 剪贴板中似乎没有任何值。

CustomClipboard.m

#import "CustomClipboard.h"


#import <UIKit/UIKit.h>
#import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h>
#import <MobileCoreServices/MobileCoreServices.h>


@implementation CustomClipboard

RCT_EXPORT_MODULE();

- (dispatch_queue_t)methodQueue
{
  return dispatch_get_main_queue();
}

RCT_EXPORT_METHOD(setString:(NSString *)content)
{
  UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
  NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding];
  [clipboard setData:data forPasteboardType:(NSString *)kUTTypeHTML];
}

RCT_EXPORT_METHOD(getString:(RCTPromiseResolveBlock)resolve
                  reject:(__unused RCTPromiseRejectBlock)reject)
{
  UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
  resolve((clipboard.string ? : @""));
}

@end

CustomClipboard.h

#import <React/RCTBridgeModule.h>

@interface CustomClipboard : NSObject <RCTBridgeModule>

@end

原生模块在JS中导入通过:

import { NativeModules } from 'react-native';

export default NativeModules.CustomClipboard;

并通过剪贴板模块暴露给应用程序的其余部分:

import NativeClipboard from './NativeClipboard';

/**
 * `Clipboard` gives you an interface for setting and getting content from Clipboard on both iOS and Android
 */
export const Clipboard = {
  /**
   * Get content of string type, this method returns a `Promise`, so you can use following code to get clipboard content
   * ```javascript
   * async _getContent() {
   *   var content = await Clipboard.getString();
   * }
   * ```
   */
  getString(): Promise<string> {
    return NativeClipboard.getString();
  },
  /**
   * Set content of string type. You can use following code to set clipboard content
   * ```javascript
   * _setContent() {
   *   Clipboard.setString('hello world');
   * }
   * ```
   * @param the content to be stored in the clipboard.
   */
  setString(content: string) {
    NativeClipboard.setString(content);
  },
};

所有代码似乎都运行正确,但没有值被复制到剪贴板。我还没有找到任何已知的错误。有什么想法吗?

【问题讨论】:

  • 根据您的 cmets 和编辑,我更新了我的答案 - 请查看更新后的答案。

标签: ios objective-c react-native uipasteboard


【解决方案1】:

编辑

这是第二次尝试。早些时候我知道您想将 HTML 复制并粘贴为文本,但是在您对问题进行 cmets 和编辑之后,我现在了解到您有一些使用 HTML 格式化的文本,并且您希望在保留格式的同时粘贴该文本。

或多或少如下所示。

这也说明了如何在故事板中连接它。这是代码。

#import "ViewController.h"

#import <MobileCoreServices/UTCoreTypes.h>
#import <WebKit/WebKit.h>

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UILabel    * statusLabel;
@property (weak, nonatomic) IBOutlet WKWebView  * webView;
@property (weak, nonatomic) IBOutlet UITextView * textView;

@end

@implementation ViewController

#pragma mark -
#pragma mark Actions

- (IBAction)reloadButtonAction:(id)sender {

    [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.webfx.com/blog/images/assets/cdn.sixrevisions.com/0435-01_html5_download_attribute_demo/samp/htmldoc.html"]]];

}

- (IBAction)copyButtonAction:(id)sender {

    if ( self.webView.loading ) {

        self.statusLabel.text = @"Loading";

    } else {

        self.statusLabel.text = @"Copying ...";

        [self.webView evaluateJavaScript:@"document.documentElement.outerHTML.toString()"
                   completionHandler: ^ ( id content, NSError * error ) {

            if ( error ) {

                self.statusLabel.text = @"Error";

            } else if ( [content isKindOfClass:NSString.class] ) {

                [UIPasteboard.generalPasteboard setData:[( NSString * ) content dataUsingEncoding:NSUTF8StringEncoding]
                                  forPasteboardType:( NSString * ) kUTTypeHTML];
                self.statusLabel.text = @"Copied HTML";

            } else {

                self.statusLabel.text = @"Unknown type";

            }

        }];

    }

}

- (IBAction)pasteButtonAction:(id)sender {


    if ( [UIPasteboard.generalPasteboard containsPasteboardTypes:@[( NSString * ) kUTTypeHTML]] ) {

        // Convert to attributed string
        NSError * cvtErr;
        NSData  * data = [UIPasteboard.generalPasteboard dataForPasteboardType:( NSString * ) kUTTypeHTML];
        NSAttributedString * attr = [[NSAttributedString alloc] initWithData:data
                                         options:@{ NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType }
                                  documentAttributes:NULL
                                           error: & cvtErr];

        if ( cvtErr ) {

            self.statusLabel.text = @"Convert error";

        } else if ( attr ) {

            self.statusLabel.text = @"Attributed";
            self.textView.attributedText = attr;

        } else {

            self.statusLabel.text = @"Unable to decode";

        }

    } else {

        NSString * content = UIPasteboard.generalPasteboard.string;

        if ( content ) {

            // Paste plain text
            self.textView.text = content;
            self.statusLabel.text = @"Pasted";

        } else {

            self.statusLabel.text = @"No string content";

        }

    }

}

@end

代码与之前非常相似。问题是 HTML = 纯文本,因此复制 HTML 并保留样式或格式也取决于您将其粘贴到的目标应用程序。该目标应用需要正确处理 HTML 以实现您想要实现的目标。

总之,这里是之前的变化。

在复制方面:文本现在被复制为 HTML 而不是字符串。除了现在和以前不同,字符串被转换为数据并且类型被标记为kUTTypeHTML之外,几乎没有什么区别。

在粘贴方面:真正的区别在这里。如果粘贴板包含 HTML,则使用属性字符串对其进行检索和格式化。如果您例如,它也可以正常工作。粘贴到 Notes 顺便说一句。

这说明了您在此处处理的问题。我也可以使用我之前使用的代码,一切都会正常工作,但我会得到未格式化的/源 HTML 而不是格式化的 HTML。

这里我假设您对相当简单但格式化的文本感兴趣。你没有特别提到 text 并且如果你例如想要复制一个 HTML 格式的表格,那么我的示例还不够,因为我使用 UITextView 作为目标。

对于更复杂的 HTML,我也会使用 WKWebView 作为目标,并将其 HTML 字符串设置为粘贴在 HTML 中的内容,但这再次表明目标应用程序还需要如何合作以正确处理 HTML。

最后,查看更新的答案和您的代码很难发现差异,所以我怀疑您的问题可能更早出现在 content 本身中

RCT_EXPORT_METHOD(setString:(NSString *)content)

更新

我还查看了您提到的 git 存储库。在那里我发现了以下内容

RCT_EXPORT_METHOD(hasString:(RCTPromiseResolveBlock)resolve
                  reject:(__unused RCTPromiseRejectBlock)reject)
{
  BOOL stringPresent = YES;
  UIPasteboard *clipboard = [UIPasteboard generalPasteboard];
  if (@available(iOS 10, *)) {
    stringPresent = clipboard.hasStrings;
  } else {
    NSString* stringInPasteboard = clipboard.string;
    stringPresent = [stringInPasteboard length] == 0;
  }

  resolve([NSNumber numberWithBool: stringPresent]);
}

看起来不对,我觉得应该是一行

stringPresent = [stringInPasteboard length] != 0;

HTH

【讨论】:

  • 不幸的是,这不是我想要的。我已经有了 HTML 字符串,我想将 HTML 复制到格式化的剪贴板,以便在将其粘贴到另一个应用程序时保持样式。将我现有的代码更改为复制为字符串完全符合您的示例所示,但要简单得多,但这不是我想要的。
  • 好的,但复制和粘贴字符串不应更改其格式或样式。 HTML 已经格式化/样式化,并且在复制和粘贴时丢失了它?这种格式或样式是什么意思?缩进?语法高亮?在任何一种情况下,目标应用程序都可以在您粘贴时重新格式化,因此剪贴板可能不是罪魁祸首。
  • 感谢您的回答。这为我指明了正确的方向!
【解决方案2】:

我在这里找到了答案:http://www.andrewhoyer.com/converting-html-to-nsattributedstring-copy-to-clipboard/

字符串在放入剪贴板之前需要转换为RTF。

RCT_EXPORT_METHOD(setString:(NSString *)content)
{
  // Source: http://www.andrewhoyer.com/converting-html-to-nsattributedstring-copy-to-clipboard/
  
  UIPasteboard *clipboard = [UIPasteboard generalPasteboard];

  // Sets up the attributes for creating Rich Text.
  NSDictionary *documentAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                      NSRTFTextDocumentType, NSDocumentTypeDocumentAttribute,
                                      NSCharacterEncodingDocumentAttribute, [NSNumber numberWithInt:NSUTF8StringEncoding],
                                      nil];
  
   
  // Create the attributed string using options to indicate HTML text and UTF8 string as the source.
  NSAttributedString *atr = [[NSAttributedString alloc]
                              initWithData: [content dataUsingEncoding:NSUTF8StringEncoding]
                              options: @{
                                  NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                  NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)
                              }
                              documentAttributes:nil error:nil];
    
  // Generate the Rich Text data using the attributed string and the previously created attributes.
  NSData *rtf = [atr dataFromRange:NSMakeRange(0, [atr length]) documentAttributes:documentAttributes error:nil];
  
  // Set the Rich Text to the clipboard using an RTF Type
  // Note that this requires <MobileCoreServices/MobileCoreServices.h> to be imported
  [clipboard setData:rtf forPasteboardType:(NSString*)kUTTypeRTF];
      
}

【讨论】:

    猜你喜欢
    • 2018-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-01
    • 1970-01-01
    • 2015-02-03
    相关资源
    最近更新 更多