iOS開發(fā)之UIMenuController使用示例詳解
簡介
UIMenuController 是一個菜單編輯界面,在很多地方都能用到,通常用于剪切、復(fù)制、粘貼、選擇、全選和刪除命令等,也可以自定義想要的操作,它長這樣:
接口介紹
open class UIMenuController : NSObject { open class var shared: UIMenuController { get } open var isMenuVisible: Bool // default is NO @available(iOS, introduced: 3.0, deprecated: 13.0, message: "Use showMenuFromView:rect: or hideMenuFromView: instead.") open func setMenuVisible(_ menuVisible: Bool, animated: Bool) @available(iOS, introduced: 3.0, deprecated: 13.0, message: "Use showMenuFromView:rect: instead.") open func setTargetRect(_ targetRect: CGRect, in targetView: UIView) @available(iOS 13.0, *) open func showMenu(from targetView: UIView, rect targetRect: CGRect) @available(iOS 13.0, *) open func hideMenu(from targetView: UIView) @available(iOS 13.0, *) open func hideMenu() @available(iOS 3.2, *) open var arrowDirection: UIMenuController.ArrowDirection // default is UIMenuControllerArrowDefault @available(iOS 3.2, *) open var menuItems: [UIMenuItem]? // default is nil. these are in addition to the standard items open func update() open var menuFrame: CGRect { get } } open class UIMenuItem : NSObject { public init(title: String, action: Selector) open var title: String open var action: Selector }
從接口中可以看出 UIMenuController 應(yīng)該使用它的單例對象,具體應(yīng)該怎么使用它呢?我們先來看一下 API 文檔對 UIMenuController 的說明:
The singleton UIMenuController instance is referred to as the editing menu. When you make this menu visible, UIMenuController positions it relative to a target rectangle on the screen; this rectangle usually defines a selection. The menu appears above the target rectangle or, if there is not enough space for it, below it. The menu’s pointer is placed at the center of the top or bottom of the target rectangle, as appropriate. Be sure to set the tracking rectangle before you make the menu visible. You are also responsible for detecting, tracking, and displaying selections.
The UIResponderStandardEditActions informal protocol declares methods that are invoked when the user taps a menu command. The canPerformAction(_:withSender:) method of UIResponder is also related to the editing menu. A responder implements this method to enable and disable commands of the editing menu just before the menu is displayed. You can force this updating of menu commands’ enabled state by calling the update() method.
You can also provide your own menu items via the menuItems property. When you modify the menu items, you can use the update() method to force the menu to update its display.
翻譯如下:
UIMenuController 單例稱為編輯菜單。當(dāng)你使這個菜單可見時,UIMenuController 將它相對于屏幕上的目標(biāo)矩形定位;這個矩形通常定義一個選擇。菜單顯示在目標(biāo)矩形上方,如果沒有足夠的空間,則顯示在其下方。菜單指針放置在目標(biāo)矩形頂部或底部的中心,視情況而定。確保在使菜單可見之前設(shè)置跟蹤矩形。您還負(fù)責(zé)檢測、跟蹤和顯示選擇。
UIResponderStandardEditActions 協(xié)議聲明了在用戶點擊菜單命令時調(diào)用的方法。 UIResponder 的 canPerformAction(_:withSender:) 方法也和編輯菜單有關(guān)。響應(yīng)者實現(xiàn)此方法以在菜單顯示之前啟用和禁用編輯菜單的命令。您可以通過調(diào)用 update() 方法強制更新菜單命令的啟用狀態(tài)。
您還可以通過 menuItems 屬性提供您自己的菜單項。修改菜單項時,可以使用 update() 方法強制菜單更新其顯示。
使用探索
根據(jù) API 說明可知
- UIMenuController 顯示位置可以通過設(shè)置一個矩形來定位
- 要想顯示 UIMenuController,需要成為響應(yīng)者
- 如果沒有設(shè)置 menuItems 時有自己默認(rèn)的菜單,也可以通過 menuItems 添加自己的菜單
如何創(chuàng)建并顯示 UIMenuController
首先,API 說的很清楚,UIMenuController 是單例,直接使用 UIMenuController.shared 即可,然后調(diào)用 open func showMenu(from targetView: UIView, rect targetRect: CGRect) 方法來顯示,
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { let menu = UIMenuController.shared menu.showMenu(from: view, rect: CGRect(x: 50, y: 50, width: 20, height: 20)) }
運行代碼發(fā)現(xiàn)并沒有什么反應(yīng),回看 API,還需要設(shè)置第一響應(yīng)者
override var canBecomeFirstResponder: Bool { true } // 上文提到的其他代碼忽略
運行代碼還是沒反應(yīng),回看 API,UIResponder 的 canPerformAction(_:withSender:) 方法也和編輯菜單有關(guān)。響應(yīng)者實現(xiàn)此方法以在菜單顯示之前啟用和禁用編輯菜單的命令,我們實現(xiàn)一下試試
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { true } // 上文提到的其他代碼忽略
當(dāng)當(dāng)當(dāng)當(dāng),成功了?。?!
實現(xiàn) Item 點擊事件
接下來,我鼠標(biāo)輕輕的點在了菜單上 Cut,結(jié)果奔潰了:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SwiftTestiOS.ViewController cut:]: unrecognized selector sent to instance 0x7fec7300d480'
根據(jù)提示,我們實現(xiàn) cut 方法。
override func cut(_ sender: Any?) { print("cut cut cut !!!") } // 上文提到的其他代碼忽略
nice,沒有奔潰,成功打印 cut cut cut !!!
其他的菜單也可以添加對應(yīng)的實現(xiàn),哈哈哈...搞定!!!
菜單 Item 太多???
問題:我不需要這么多菜單,咋整?
之前沒有因為沒有實現(xiàn) canPerformAction(_:withSender:) 方法時,UIMenuController 無法出現(xiàn),實現(xiàn)了之后就出現(xiàn)了一大堆菜單,此方法有一個action參數(shù),是不是此方法決定了哪些action可以顯示呢,試試看:
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { let actions = [#selector(cut(_:)), #selector(copy(_:)), #selector(paste(_:)), #selector(delete(_:))] return actions.contains(action) } // 上文提到的其他代碼忽略
也就是說,canPerformAction(_:withSender:) 決定設(shè)置的哪些菜單可以生效。敲黑板,這點很重要?。?!
UIResponderStandardEditActions 協(xié)議
根據(jù) API 說明,UIResponderStandardEditActions 協(xié)議定義了 UIMenuController 的一些系統(tǒng)默認(rèn)方法,內(nèi)容如下
public protocol UIResponderStandardEditActions : NSObjectProtocol { @available(iOS 3.0, *) optional func cut(_ sender: Any?) @available(iOS 3.0, *) optional func copy(_ sender: Any?) @available(iOS 3.0, *) optional func paste(_ sender: Any?) @available(iOS 3.0, *) optional func select(_ sender: Any?) @available(iOS 3.0, *) optional func selectAll(_ sender: Any?) @available(iOS 3.2, *) optional func delete(_ sender: Any?) //... 其他方法略 }
而且上文實現(xiàn) cut 方法的時候有override,也就是 UIViewController 有這個方法,根據(jù)線索可以查到對應(yīng)關(guān)系
UIViewController
UIResponder
UIResponderStandardEditActions
添加自定義菜單
如果系統(tǒng)提供的菜單不滿足我們自己的需求,可以通過 menuItems 添加自定義菜單
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { let menu = UIMenuController.shared let item1 = UIMenuItem(title: "hello", action: #selector(helloAction)) let item2 = UIMenuItem(title: "world", action: #selector(worldAction)) menu.menuItems = [item1, item2] menu.showMenu(from: view, rect: CGRect(x: 50, y: 50, width: 20, height: 20)) } @objc func helloAction() { print(#function) } @objc func worldAction() { print(#function) } override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { let actions = [#selector(cut(_:)), #selector(copy(_:)), #selector(paste(_:)), #selector(delete(_:)), #selector(helloAction), #selector(worldAction)] return actions.contains(action) } // 上文提到的其他代碼忽略
添加之后效果如下:
箭頭的方向
UIMenuController 有個arrowDirection 屬性,用于設(shè)置箭頭的位置,它是ArrowDirection 類型的枚舉
extension UIMenuController { public enum ArrowDirection : Int, @unchecked Sendable { case `default` = 0 case up = 1 case down = 2 case left = 3 case right = 4 } }
默認(rèn)的時候,會根據(jù)需要調(diào)整箭頭方向,而箭頭的位置根據(jù) API 描述(菜單指針放置在目標(biāo)矩形頂部或底部的中心,視情況而定)是在設(shè)置的矩形區(qū)域的上下邊的中間位置。
注意:如果強制設(shè)定了一個方向的話,而在該方向沒有足夠的空間,則不會顯示菜單
實際使用
在顯示 UIMenuController 的時候有一個方法 open func showMenu(from targetView: UIView, rect targetRect: CGRect),此方法主要是用來設(shè)置顯示位置的,targetView 指明位置參照對象,rect 表示參照 targetView 的位置,如:
let position = label.bounds menu.showMenu(from: label, rect: position)
上述代碼可以理解成:菜單顯示在相對于 label 的 position 處
總結(jié)
UIMenuController 的使用總體還是比較簡單的,主要是以下幾點:
UIMenuController 是單例
顯示默認(rèn)的 UIMenuController 菜單需要
- 成為第一響應(yīng)者
- UIResponder 的 canPerformAction(_:withSender:)返回可以添加的 UIMenuItem
- 實現(xiàn)對應(yīng) UIMenuItem 的方法
自定義 UIMenuItem
- 通過 UIMenuController.menuItems 屬性添加自定義的 UIMenuItem
- 在 canPerformAction(_:withSender:) 對應(yīng) UIMenuItem 的 action
箭頭的位置
- 建議使用 ArrowDirection.default
- 使用其他值可能會看不見菜單,除非確定一定可以顯示,否則不推薦使用
以上就是iOS開發(fā)之UIMenuController使用示例詳解的詳細(xì)內(nèi)容,更多關(guān)于iOS開發(fā)UIMenuController的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
iOS中定位當(dāng)前位置坐標(biāo)及轉(zhuǎn)換為火星坐標(biāo)的方法
這篇文章主要介紹了iOS中獲取當(dāng)前位置坐標(biāo)及轉(zhuǎn)換為火星坐標(biāo)的方法,這里的火星坐標(biāo)指的是我國專門研制的一種加密的坐標(biāo)系統(tǒng)...需要的朋友可以參考下2016-02-02iOS應(yīng)用開發(fā)中的文字選中操作控件UITextView用法講解
這篇文章主要介紹了iOS應(yīng)用開發(fā)中的文字選中操作控件UITextView用法講解,代碼基于傳統(tǒng)的Objective-C語言,需要的朋友可以參考下2016-02-02iOS 10撥打系統(tǒng)電話彈出框延遲出現(xiàn)問題的解決
iOS10的到來,帶來了條幅和鎖屏界面的重新設(shè)計,美觀又好看,再加上抬腕喚醒功能,查看需要的信息確實更便捷了,還能快捷回復(fù)一些通知,十分輕松,但同樣有問題,下面這篇文章主要給大家介紹了關(guān)于iOS 10撥打系統(tǒng)電話彈出框延遲出現(xiàn)問題的解決方法,需要的朋友可以參考下。2017-10-10