1. 程式人生 > >iOS webview,WKWebView長按彈出框UIMenuController新增自定義功能

iOS webview,WKWebView長按彈出框UIMenuController新增自定義功能

-先說下背景,`UIMenuController`類基於<UIKit>框架下,是iOS3.0之後釋出的,它是一個長按撥出框,自帶有複製、剪下、貼上、全選、刪除等等功能。文件裡面定義如下圖

****本次要實現的功能是 在載入了HTML字串的UIWebView中實現自定義長按撥出框。

1.預設情況下,UITextFiled、UITextView、UIWebView這三個類及其子類都有自帶的長按彈出UIMenuController功能,所有除了這三個以外,有需求的需要自己新增手勢實現,實現方法與本文談到的大同小異。

2.要使當前頁面成為第一響應者。

3.遮蔽系統自帶的彈出功能

*********下面開始

>首先建立UIMenuController,可以在需要實現的webview的構造方法裡面建立程式碼如下:(此處我根據需求將UIWebView需要載入的內容由初始化賦值,並在呼叫屬性setter方法的時候加載出來)

- (instancetype)initWithFrame:(CGRect)frame content:(NSString *)content
{
    self = [super initWithFrame:frame];
    if (self) {
        
        [self becomeFirstResponder];
        
        [self createMenu];
        
        self.content = content;
    }
    return self;
}


//構建UIMenuController
- (void)createMenu {

    UIMenuController *menu = [UIMenuController sharedMenuController];
    UIMenuItem *item0 = [[UIMenuItem alloc] initWithTitle:@"複製" action:@selector(copy:)];
    UIMenuItem *item1 = [[UIMenuItem alloc] initWithTitle:@"全選" action:@selector(selectAll:)];
    UIMenuItem *item2 = [[UIMenuItem alloc] initWithTitle:@"列印選中文字" action:@selector(logSelectedText:)];
    
    [menu setMenuItems:@[
                         item0,
                         item1,
                         item2]];
}

//內容的setter方法裡面載入
- (void)setContent:(NSString *)content {
    
    _content = content;
    [self loadHTMLString:content baseURL:nil];
}


>注意要彈出UIMenuController必須完成三個步驟

 1.在UIWebView中呼叫becomeFirstResponder方法。

 2.在UIWebView中重寫父類方法canBecomeFirstResponder並且 return Yes。

 3.在UIWebView中重寫canPerformAction:withSender:方法,來篩選出需要響應的item

 第三點實現程式碼如下:

//指定menu響應事件遮蔽系統自帶響應事件

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {

if (action ==@selector

(copy:) ||

        action == @selector(selectAll:) ||

        action == @selector(logSelectedText:)) {

returnYES;

    }

returnNO;

}

>截止此處,就可以看到彈出的UIMenuController了,效果圖如下:


>那麼現在還存在倆個問題

 1.賦值和全選都出現了倆次,且他們都是有效的。

 2.列印選中文字還沒實現,也沒找到API直接獲取這個選中文字的。

>針對問題1 是因為系統自帶的和我們自定義都篩選出來了,解決辦法是可以在我們開始的UIMenuController生成的地方刪除這倆個重複的UIMenuItem.

 那麼此處涉及到它會顯示"copy" "cut"倆個英文。如果支援中文需要到工程中配置一下Project->Info->Localizations裡面找到Chinese(simplified)並新增。

 此處注意這樣新增後顯示的語言是根據手機或者模擬器設定的語言而定的。 配置如下:


>針對問題2  首先需要講一下 cut: copy:等系統自帶的方法,這些方法通通放在了UIResponderStandardEditActions協議中.具體有以下方法:

//    @protocol UIResponderStandardEditActions <NSObject>
//    @optional
//    - (void)cut:(nullable id)sender NS_AVAILABLE_IOS(3_0); //剪下
//    - (void)copy:(nullable id)sender NS_AVAILABLE_IOS(3_0); //複製
//    - (void)paste:(nullable id)sender NS_AVAILABLE_IOS(3_0); //貼上
//    - (void)select:(nullable id)sender NS_AVAILABLE_IOS(3_0); //選擇
//    - (void)selectAll:(nullable id)sender NS_AVAILABLE_IOS(3_0); //全選
//    - (void)delete:(nullable id)sender NS_AVAILABLE_IOS(3_2);    //刪除
//    - (void)makeTextWritingDirectionLeftToRight:(nullable id)sender NS_AVAILABLE_IOS(5_0); //改變書寫模式為從左向右按鈕觸發
//    - (void)makeTextWritingDirectionRightToLeft:(nullable id)sender NS_AVAILABLE_IOS(5_0); //改變書寫模式為從右向左按鈕觸發
//    
//    //以下方法呼叫會導致程式崩潰  具體原因再研究
//    - (void)toggleBoldface:(nullable id)sender NS_AVAILABLE_IOS(6_0);
//    - (void)toggleItalics:(nullable id)sender NS_AVAILABLE_IOS(6_0);
//    - (void)toggleUnderline:(nullable id)sender NS_AVAILABLE_IOS(6_0);
//    
//    - (void)increaseSize:(nullable id)sender NS_AVAILABLE_IOS(7_0);
//    - (void)decreaseSize:(nullable id)sender NS_AVAILABLE_IOS(7_0);
//    
//    @end
然後就是實現我們自定義的打印出選中文字的方法。我找了一下,並有找到UIMenuController有直接的屬性獲取選中的文字,固尋求其他解決途徑。

       有倆種解決方法僅供參考:

      1.利用js程式碼直接獲取UIWebView中被選中的文字

//UIWebView自帶獲取選中文字的方法
- (NSString *)getSelectedText {

    return [self stringByEvaluatingJavaScriptFromString:@"window.getSelection().toString()"];
}
2.利用UIMenuController帶的賦值方法將內容賦值到貼上板,再從貼上板獲得所需內容
    //先複製到貼上板 再列印貼上板上的內容
    [self copy:menu];
    NSLog(@"選中的文字為:%@", [UIPasteboard generalPasteboard].string);

----------------17.11.6更新-----------------

遇到倆三個同學問我適配WKWebView之後上面的程式碼不能很好的支援。之前幫一個同學解決了又忘記了,今天又被問到,感謝@燈紅酒綠映不出的落寞 提供的思路。

因為WKWeb用相同的方法寫會出現一個問題就是不能遮蔽系統的一些方法。

canPerformAction: withSender:
解決辦法是把UIMenuController提出來寫到ViewController層,所以在這兒需要實現一個方法就是禁止物件取消第一響應
- (BOOL)canBecomeFirstResponder {
    return YES;
}
- (BOOL)canResignFirstResponder {
    return NO;
}
這樣寫功能是實現了,但是存在一個bug.理論上一個viewcontroller被pop出navigationController的棧之後會釋放,但是這個物件似乎沒有完全消失,因為我們寫了這個物件禁止取消第一響應,其他的物件就不能再響應了。有時候這個bug也會表現為在定義著不同的UIMenuController的不同檢視中表現為同一個。所以我們在檢視消失的時候應該允許他取消第一響應,而檢視存在的時候不行。
- (BOOL)canBecomeFirstResponder {
    return YES;
}
- (BOOL)canResignFirstResponder {
    
    if (_dismissSelf) {
        
        return YES;
    }
    return NO;
}

沒有懂的同學把這個判斷取消就明白了。我在UIWebView中允許出現“拷貝,全選,列印選中文字”,在WK中允許出現“自定義複製,新增筆記”。

結合Demo,第一次用UIWebView正常,用了WK沒加上面判斷之後再看UIWebView異常



Github上面已經更新。

----------------18.1.9更新-----------------

有小夥伴問到使用WKWebView用我上面的方法做出來沒有全選功能,想要新增一個全選功能。其實很簡單我試過直接遮蔽函式中新增不遮蔽selectedAll:,但是會造成程式崩潰,有興趣的可以試試寫一個WKWebview的子類遮蔽看看。
canPerformAction: withSender:
另一種思路就是按照之前新增自定義功能的方法新增一個方法叫做“全選”。1.新增一個UIMenuItem叫做“全選”。2.在遮蔽函式中開啟我們自定義的全選。(注意方法名不要和系統方法名衝突)3.實現自定義全選。利用[WKWebView selectAll:]函式,注意實現之後把允許檢視取消第一響應的判定改一下,不然會出現全選異能取消的BUG.遺留了一個bug就是我們使用過一次全選功能之後,以後再開啟UIMenuController有幾個系統方法就不能遮蔽了(因為之後不會再呼叫[self canResignFirstResponder]方法,現在使用Wk的時候系統方法這塊確實API有一定的問題,很多實現都是遮蔽UIMenuController採用UIAlertController來替代),希望有解決辦法的小夥可以不吝賜教,謝謝!

--------------18.3.29日更新-----------

有小夥伴問到,選中執行選單項的某個功能後,選中的文字和那對藍色條都不會消失。我測試了一下確實存在這個問題。這塊之前就說過系統API是存在一定問題的,但是迫於功能需求,只能尋找一些不太完美的方法來解決。

1.剛開始想通過執行選單項的某個功能後,就用程式碼模擬一次使用者點選螢幕來取消掉選中的文字和藍色條。此處發現一些東西可以和大家分享一下

   >>方法 sendActionsForControlEvents可以對UIControl及其子類直接傳送響應,例如UIButton,可以用程式碼寫模擬點選.然而UIView       是UIControl父類,故不能使用

    

  >>方法模擬UITouch和UIEvent 呼叫 touchesBegan方法,參考文章http://www.cocoawithlove.com/2008/10/synthesizing-touch-event-on-iphone.html 。   其中涉及私有API,可能對上線有影響,感興趣的可以瞭解一下。

2.最終還是利用一個取巧的辦法,在執行選單項的某個功能後,重新載入一個web即取消了其選中的文字。

PS:有更好辦法的同學歡迎分享交流。

***以上

(歡迎隨手給一顆星星哦~)本篇部落格Demo地址https://github.com/xmy0010/DemoForCSDN

本人郵箱[email protected]歡迎小夥伴一起討論,學習,進步。





相關推薦

iOS webview,WKWebViewUIMenuController新增定義功能

-先說下背景,`UIMenuController`類基於<UIKit>框架下,是iOS3.0之後釋出的,它是一個長按撥出框,自帶有複製、剪下、貼上、全選、刪除等等功能。文件裡面定義如下圖****本次要實現的功能是 在載入了HTML字串的UIWebView中實現自定

微信公眾號頁面禁止系統選單,相容ios和安卓

最近做的一個微信公眾號需求,需要長按撥出有刪除按鈕的浮層,可是長按的時候也會彈出系統選單“選擇複製”和“在瀏覽器開啟”,這裡記錄一下解決方案。 css程式碼如下: cssSelect{ //這裡為css選擇器 -webkit-touch-callout:non

WKWebView 禁用 UIMenuController

iOS8開始使用WKWebView,但實際使用過程中和UIWebView有許多區別。比如禁止在webview中長按彈出UIMenuController的操作: UIWebView中 - (BOOL)canPerformAction:(SEL)a

android List View onCreateContextMenu 選單並獲取的View

onCreateContextMenu 的view 引數獲取的不是ListView中被選中的項,而onItemLongClick 的View 引數則是被選中的項 onItemLongClick 會在onCreateContextMenu之前被呼叫,可以先響應onItemLon

Android ListView對話方塊

ListView長按彈出對話方塊可以用PopupWindow實現 今天記錄的是重寫onCreateContextMenu和onContextItemSelected實現彈出對話方塊,比前者簡單很多

iOS開發- 自動消失的

- (void)timerFireMethod:(NSTimer*)theTimer//彈出框 { UIAlertView *promptAlert = (UIAlertView*)[theTimer userInfo]; [promptAlert dismi

iOS動態分享收藏舉報

彈出框上的按鈕分兩個陣列: 一個是在彈出框上部的滑動檢視上的按鈕,存放在shareBtnArray的陣列中; 一個是在中間螢幕寬的按鈕,存放在operationArray陣列中。 點選彈出的按鈕會觸發代理方法: #pragma mark Dele

android中RecyclerView控件實現PopupMenu菜單功能

mage 有一個 手工 sim pat 創建 .get mco span 之前寫過一篇文章:android中實現簡單的聊天功能 現在是在之前功能的基礎上,添加一個長按聊天記錄,刪除對應聊天記錄的功能 RecyclerView控件,沒有對應的長按事件,我們需要自己手工添加

移動端web 禁止的選單 Safari

這是一個系列,記錄我前端開發常用的程式碼,小常識,有些是參考網上程式碼,(講的可能有點爛,求不要打臉,嚶嚶嚶~~)送給那些需要的人。可以相互交流,喜歡的加我吧。 Wx: Lxp911221

iOS一個View(定義的提示

之前要寫一個iPad的一個專案,裡面有一個分享的按鈕,彈出一個View,上面幾個圖片按鈕,菜菜的我為了這個小功能花費了很多時間,最後弄明白了,發現之前的思路都想錯了,要實現彈出一個View,其實就是先

Android ListViewCheckBox,實現全選,反選,批量刪除功能

ListView長按彈出CheckBox,實現全選,反選,批量刪除功能. 主佈局:activity_main <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="

ListView:上下文選單(ContextMenu)

有兩種方法實現長按彈出上下文選單: 方法一: 1)註冊上下文選單 /** * 上下文選單演示Demo * * @author CodingMyWorld 2011-8-27 下午03:22:39 */ publicclass SampleContextMe

android開發之貼上,點選之後複製

設定一個長按點選事件,然後呼叫下面方法 private void ShowPopWindow(int x, int y) {         popupWindow = new PopupWindow(ViewGroup.LayoutParams.WRAP_CONTEN

Android ListView兩種選單方式

/** * @author xianglong guo 2012-05-17 22:15 * 知識點1:ListView item:兩種長按彈出選單方式 * 知識點2:ListView SimpleAdapter的使用 * 知識點 3:在java程式碼中建立一個Li

高仿微信聊天介面樣式

效果圖 背景 在公司做的專案裡面,剛好有需要用到微信聊天介面長按彈框樣式這種 UI 的。 網上找了一下,沒找到。 Android 現成的 ListPopupWindow 又不能滿足需求。 因此在非上班時間擼一個出來,供大家使用。 示例程式碼 關鍵檔案、示例程式

Android仿ios底部效果

準備: public class ActionSheet { public interface OnActionSheetSelected { void onClick(int whichButton); } private ActionShee

iOS 兩款你可能會用到的

前言好久沒寫部落格了……最近拿到了一版原型圖,各種彈框,簡直快把老爺給彈死了……因為實現功能是其次的,最主要還得把這些東西給封裝一下,方便同事的呼叫。於是乎,我就開始了爬坑的過程。經過兩天的耕耘,出了兩款風格迥異的彈框,這裡給大家分享一下。。。同時也祭奠一下,我老去的容顏……效果圖底部PickerView彈框

iOS-禁用 UIWebView 放大鏡及拷貝貼上

大家常常在開發中會碰到這樣的需求–禁用網頁或PDF檔案中放大鏡及拷貝貼上彈出框 查詢了很多資料發現如下方法已經失效 webView.stringByEvaluatingJavaScriptFromS

iOS 簡單的loading實現

————-LoadingAlerter.h———– // // LoadingAlerter.h // SdkModle // // Created by Sean on 15/2/10. // Copyright (c) 2015年 Feiyu.

ios 底部選單

在IOS開發中,經常用到底部選單,這是一個簡單的底部彈出選單的實現程式碼如下:      標頭檔案(.h)     #import <UIKit/UIKit.h> @interface ShareMenuView : UIView { UIButto