使用 React Native 将 HTML 复制到 UIPasteboard
Copy HTML to UIPasteboard with React Native
我有一个简单的 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);
},
};
代码似乎 运行 正确,但没有值被复制到剪贴板。我还没有找到任何已知的错误。有什么想法吗?
编辑
这是第二次尝试。早些时候我了解到您想将 HTML 复制并粘贴为文本,但在您对问题进行评论和编辑后,我现在了解到您有一些使用 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 顺便说一句。
这说明了您在这里处理的问题。我也可以使用我之前使用的代码,一切都可以正常工作,但我会得到未格式化的 / source HTML.
而不是格式化的 HTML
这里我假设您对相当简单但格式化的文本感兴趣。您没有具体提及 text,例如,如果您想复制一个 HTML 格式的 table 那么我的例子是不够的,因为我使用 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
我在这里找到了答案: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];
}
我有一个简单的 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);
},
};
代码似乎 运行 正确,但没有值被复制到剪贴板。我还没有找到任何已知的错误。有什么想法吗?
编辑
这是第二次尝试。早些时候我了解到您想将 HTML 复制并粘贴为文本,但在您对问题进行评论和编辑后,我现在了解到您有一些使用 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 顺便说一句。
这说明了您在这里处理的问题。我也可以使用我之前使用的代码,一切都可以正常工作,但我会得到未格式化的 / source HTML.
而不是格式化的 HTML这里我假设您对相当简单但格式化的文本感兴趣。您没有具体提及 text,例如,如果您想复制一个 HTML 格式的 table 那么我的例子是不够的,因为我使用 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
我在这里找到了答案: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];
}