iOS create "copy paste" like popover (UIMenuController) in UITableView - ios

Im trying to create a intractable pop over similar to how apples "copy", "paste". Im not sure how to do it.
Is there a 3rd party lib or is it a native component ?

If it helps anyone, the keyword to look for is "UIMenuController"
I finally managed to solve this by implementing a custom UITableViewCell
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
let showPasswordItem = UIMenuItem(title: "Show Password", action: #selector(showPass(_:)))
let copyUserNameItem = UIMenuItem(title: "Copy Username", action: #selector(copyUsername(_:)))
let copyPasswordItem = UIMenuItem(title: "Copy Password", action: #selector(copyPass(_:)))
self.isPasswordShowing = !self.password.isSecureTextEntry
UIMenuController.shared.menuItems?.removeAll()
UIMenuController.shared.menuItems = [copyPasswordItem,copyUserNameItem,showPasswordItem,hidePasswordItem]
UIMenuController.shared.update()
if selected {
self.becomeFirstResponder()
let menu = UIMenuController.shared
menu.setTargetRect(self.contentView.frame, in: self.contentView.superview!)
menu.setMenuVisible(true, animated: true)
}
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return action == #selector(showPass(_:)) || action == #selector(copyUsername(_:)) || action == #selector(copyPass(_:))
}
override var canBecomeFirstResponder : Bool {
return true
}
func showPass(_ send:AnyObject){
self.password.isSecureTextEntry = false
self.isPasswordShowing = true
}
func copyUsername(_ send:AnyObject){
UIPasteboard.general.string = self.username.text
}
func copyPass(_ send:AnyObject){
UIPasteboard.general.string = self.password.text
}
func hidePass(_ send:AnyObject){
self.password.isSecureTextEntry = true
self.isPasswordShowing = false
}

Related

UIMenuController is not Visible in Tableview

I can not able to show custom MenuController while long Press gesture on tableview cell.
My code is as follow:
override func viewDidLoad() {
super.viewDidLoad()
self.setupLongPressGesture()
}
func setupLongPressGesture() {
let longPressGesture:UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.handleLongPress))
self.tbl.addGestureRecognizer(longPressGesture)
}
#objc func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer){
if gestureRecognizer.state == .began {
let touchPoint = gestureRecognizer.location(in: self.tbl)
if let indexPath = tbl.indexPathForRow(at: touchPoint) {
let cell = tbl.cellForRow(at: indexPath) as! ChattingTextCell
self.view.becomeFirstResponder()
let copy = UIMenuItem(title: "piy", action: #selector(copy_text))
let menucontroller = UIMenuController.shared
menucontroller.menuItems = [copy]
menucontroller.update()
//menucontroller.setTargetRect(cell.frame, in: tbl)
menucontroller.setTargetRect(CGRect(x: 100, y: 200, width: 100, height: 50), in: self.view)
menucontroller.setMenuVisible(true, animated: true)
}
}
}
func canBecomeFirstResponder() -> Bool {
return true
}
func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool
{
print(action)
return true
}
I already refer to many sites but all of them saying the same thing... I can not find any good tutorial for this as well...
Please help me to solve this....

Done button in phone pad (Swift 4)

I'm new to Swift and I was creating a registration form. I have a phone text field and when I open the phone pad, I can not see the done or return button so I can dismiss the on screen keyboard with resignfirstresponder().
I used this link : https://gist.github.com/jplazcano87/8b5d3bc89c3578e45c3e
And now, I get the Done button but on pressing Done, my app is crashing because of the selector which says is unidentified. Can anyone please help or guide me with another way?
Thanks in advance
Here is the code
import UIKit
class ViewController: UIViewController, UITextFieldDelegate, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet var registerTF: [KaustabhTF]!
// MARK:- App LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK:- User Defined
func isValidEmail(email: String) -> Bool {
let emailRegEx = "^(?:(?:(?:(?: )*(?:(?:(?:\\t| )*\\r\\n)?(?:\\t| )+))+(?: )*)|(?: )+)?(?:(?:(?:[-A-Za-z0-9!#$%&’*+/=?^_'{|}~]+(?:\\.[-A-Za-z0-9!#$%&’*+/=?^_'{|}~]+)*)|(?:\"(?:(?:(?:(?: )*(?:(?:[!#-Z^-~]|\\[|\\])|(?:\\\\(?:\\t|[ -~]))))+(?: )*)|(?: )+)\"))(?:#)(?:(?:(?:[A-Za-z0-9](?:[-A-Za-z0-9]{0,61}[A-Za-z0-9])?)(?:\\.[A-Za-z0-9](?:[-A-Za-z0-9]{0,61}[A-Za-z0-9])?)*)|(?:\\[(?:(?:(?:(?:(?:[0-9]|(?:[1-9][0-9])|(?:1[0-9][0-9])|(?:2[0-4][0-9])|(?:25[0-5]))\\.){3}(?:[0-9]|(?:[1-9][0-9])|(?:1[0-9][0-9])|(?:2[0-4][0-9])|(?:25[0-5]))))|(?:(?:(?: )*[!-Z^-~])*(?: )*)|(?:[Vv][0-9A-Fa-f]+\\.[-A-Za-z0-9._~!$&'()*+,;=:]+))\\])))(?:(?:(?:(?: )*(?:(?:(?:\\t| )*\\r\\n)?(?:\\t| )+))+(?: )*)|(?: )+)?$"
let emailTest = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
let result = emailTest.evaluate(with: email)
return result
}
func isValidPassword(pass:String?) -> Bool {
let passwordTest = NSPredicate(format: "SELF MATCHES %#", "(?=.*[A-Z])(?=.*[0-9])(?=.*[a-z]).{8,}")
return passwordTest.evaluate(with: pass)
}
func displayAlert(alertMessage: String) {
let alertController = UIAlertController(title: "Alert!!", message: alertMessage, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel,handler: nil))
self.present(alertController, animated: true, completion: nil)
}
func addDoneButtonOnKeyboard()
{
let doneToolbar: UIToolbar = UIToolbar()
doneToolbar.barStyle = UIBarStyle.default
let flexSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.done, target: self, action: Selector(("doneButtonAction")))
var items = [UIBarButtonItem]()
items.append(flexSpace)
items.append(done)
doneToolbar.items = items
doneToolbar.sizeToFit()
self.registerTF[5].inputAccessoryView = doneToolbar
}
func doneButtonAction()
{
self.registerTF[5].resignFirstResponder()
}
func isValidPhone(value: String) -> Bool {
let PHONE_REGEX = "^[0-9]{10}$"
let phoneTest = NSPredicate(format: "SELF MATCHES %#", PHONE_REGEX)
let result = phoneTest.evaluate(with: value)
return result
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true) //This will hide the keyboard
}
let myPickerData = [String](arrayLiteral: "+91", "+971", "+1", "+121", "+80", "+00")
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView( _ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return myPickerData.count
}
func pickerView( _ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return myPickerData[row]
}
func pickerView( _ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
registerTF[4].text = myPickerData[row]
}
func nextTextFieldToFirstResponder(textField: KaustabhTF) {
if textField.absoluteCount() == 0
{
displayAlert(alertMessage: "Please enter the apt value.")
}
let senderTag = textField.tag
if senderTag == 0 // Name
{
registerTF[senderTag + 1].becomeFirstResponder()
}
else if senderTag == 1 // Email
{
if !isValidEmail(email: textField.text!)
{
displayAlert(alertMessage: "Please input your valid email address")
}
else
{
registerTF[senderTag + 1].becomeFirstResponder()
}
}
else if senderTag == 2 // Password
{
if !isValidPassword(pass: textField.text)
{
displayAlert(alertMessage: "Please input a password with minimum of 8 characters including an upper case character, a lower case character and a digit.")
}
else
{
registerTF[senderTag + 1].becomeFirstResponder()
}
}
else if senderTag == 3 // Confirm Password
{
if !isValidPassword(pass: textField.text)
{
displayAlert(alertMessage: "Please input a password with minimum of 8 characters including an upper case character, a lower case character and a digit.")
}
else if registerTF[senderTag-1].text != registerTF[senderTag].text
{
displayAlert(alertMessage: "Password and confirm password dont match. Please try again")
}
else
{
textField.resignFirstResponder()
}
}
else if senderTag == 4 //Country Code
{
let thePicker = UIPickerView()
registerTF[4].inputView = thePicker
thePicker.delegate = self
}
else if senderTag == 5 // Phone Number
{
self.addDoneButtonOnKeyboard()
if !isValidPhone(value: textField.text!)
{
displayAlert(alertMessage: "Please make sure that the phone number is correct")
}
doneButtonAction()
}
}
Replace
Selector(("doneButtonAction"))
with
#selector(ViewController.doneButtonAction)
And complete code will be:
let done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.done, target: self, action: #selector(ViewController.doneButtonAction))
And add #objc before your doneButtonAction method and final code will be:
#objc func doneButtonAction()
{
self.registerTF[5].resignFirstResponder()
}
For more info refer THIS.
In swift 3.0 and 4.0 you can used this code.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var txtNumber: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.addDoneButtonOnKeyboard()
}
func addDoneButtonOnKeyboard()
{
let doneToolbar: UIToolbar = UIToolbar(frame: CGRect.init(x: 0, y: 0, width: 320, height: 50))
doneToolbar.barStyle = UIBarStyle.default
let flexSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.done, target: self, action: #selector(doneButtonAction))
var items = [UIBarButtonItem]()
items.append(flexSpace)
items.append(done)
doneToolbar.items = items
doneToolbar.sizeToFit()
self.txtNumber.inputAccessoryView = doneToolbar
}
**//button action generate following two way both are working great but use any one**
#objc func doneButtonAction()
{
self.txtNumber.resignFirstResponder()
}
#IBAction func doneButtonAction(_ sender: UIButton) {
self.txtNumber.resignFirstResponder()
}
}
Possible Duplicate of this post: How to show "Done" button on iPhone number pad
I have tried to add a duplicate flag, However I could not do it for some reason. Hope it will be helpful for others.

UIMenuController do not hide system items

I show a UIMenuController in a uiviewconroller in this way:
in my class:
override open func canBecomeFirstResponder() -> Bool {
return true
}
open override func canPerformAction(_ action: Selector, withSender sender: Any) -> Bool {
//here I check for my custom action, else return false
return false
}
then to show I use:
//Make this as first responder
self.becomeFirstResponder()
///Build menu
let menu = UIMenuController.shared
///Set item and anchor point, and showit
menu.menuItems = itemsToAdd
menu.setTargetRect(CGRect(x: 0, y: 5, width: bubbleNode.view.bounds.size.width, height: bubbleNode.view.bounds.size.height), in: bubbleImageNode.view)
menu.setMenuVisible(true, animated: true)
the problem is that in a device I show my custom items, but also: "Spell, speak, speck sentence, ecc..." how can I disable it?
override canPerformAction and handle it for each specific action. It working perfectly for me.
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
var canPerform = super.canPerformAction(action, withSender: sender)
if (action == "your action to restrict") {
canPerform = false
}
return canPerform
}
Ok, the problem is "Accessibility" option "speak selection" in my device, if I disabled it, I see only the custom items, but in other app i see only custom items with this option enabled!
class TextViewWithCopyAction: UITextView {
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(copy(_:)){
return true
} else {
return false
}
}
}
to use it:
let textView: TextViewWithCopyAction = {
let textView = TextViewWithCopyAction()
textView.backgroundColor = .white
return textView
}()
If you see speak option in UIMenuController that is from device go to:
Setting->Accessibility->Speech->Speech
Select and disable it. Automatically Speak option will get removed from menu.

Selector in UIBarButtonItem not calling

I have a selector on my UIBarButton referencing a function to segue to another view controller but the function never gets called when clicked on. Through testing breakpoints I can see the function, segueToCartViewController, never gets called.
Thanks in advance!
UIBarButtonItem init
private let reuseIdentifier = "ItemCell"
private let SegueCartIdentifier = "CatalogToCart"
final class CatalogViewController: UICollectionViewController {
//MARK: -properties
var brand: Brand!
var cart: [Item]!
fileprivate let itemsPerRow:CGFloat = 3
fileprivate let sectionInsets = UIEdgeInsets(top: 30, left: 20, bottom: 30, right: 20)
private var cartItem: UIBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "Cart"), style: .plain, target: self, action: #selector(segueToCartViewController(_:)))
var selectedItemIndexPath: IndexPath?{
didSet{
var indexPaths = [IndexPath]()
if let selectedItemIndexPath = selectedItemIndexPath{
indexPaths.append(selectedItemIndexPath)
}
if let oldValue = oldValue{
indexPaths.append(oldValue)
}
collectionView?.performBatchUpdates({
self.collectionView?.reloadItems(at: indexPaths)
}) { completed in
if let selectedItemIndexPath = self.selectedItemIndexPath{
self.collectionView?.scrollToItem(at: selectedItemIndexPath, at: .centeredVertically, animated: true)
}
}
}
}
override func viewDidAppear(_ animated: Bool) {
self.navigationController?.setToolbarHidden(false, animated: false)
let flex = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
self.navigationController?.toolbar.items = [flex,cartItem,flex]
}
}
call for segue
//MARK: CartNavigation
extension CatalogViewController: CartDelegate{
func segueToCartViewController(_ sender: AnyObject){
super.performSegue(withIdentifier: SegueCartIdentifier, sender: sender)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let destination = segue.destination as? UINavigationController else{
return
}
cartVC.delegate = self
}
func closeModallyPresentedViewController() {
dismiss(animated: true, completion: nil)
}
}
The target of your UIBarButtonItem is nil because self is nil during it's initialization.
You can initialize it like this instead
final class CatalogViewController: UICollectionViewController {
lazy final private var cartItem: UIBarButtonItem = { [unowned self] in
return UIBarButtonItem(image: #imageLiteral(resourceName: <#T##String#>), style: .plain, target: self, action: #selector(segueToCartViewController(_:)))
}()
override function viewDidAppear(_ animated: Bool) {
//blah blah, the rest of your code
}
}
See here for a good explanation about the value of self during initialization of properties.

How to use More menucontroller in View controller

Can Anyone help me, i'm having problem with UIMenucontroller.In here, i have to use two menucontroller in single viewcontroller.
For First menu only "paste",for other menu "copy","select","select all" When i'm using shared menucontroller it affects the other menu.
My code for first menu is as follows:
override func canBecomeFirstResponder() -> Bool
{
return true
}
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool
{
//actions
}
UIMenuController.sharedMenuController().menuItems = nil
let Select: UIMenuItem = UIMenuItem(title: "Select", action: Selector("Select"))
let SelectAll: UIMenuItem = UIMenuItem(title: "SelectAll", action: Selector("SelectAll"))
let Copy: UIMenuItem = UIMenuItem(title: "Copy", action: Selector("Copy"))
let menu: UIMenuController = UIMenuController.sharedMenuController()
menu.menuItems = [Select,SelectAll,Copy]
menu.setTargetRect(cell.frame, inView: cell.superview!)
menu.setMenuVisible(true, animated: true)
and my second menu is:
UIMenuController.sharedMenuController().menuVisible = false
let paste: UIMenuItem = UIMenuItem(title: "Paste", action: Selector("paste"))
let menu: UIMenuController = UIMenuController.sharedMenuController()
menu.menuItems = [paste]
menu.setTargetRect(message_Textfield.frame, inView: message_Textfield.superview!)
menu.setMenuVisible(true, animated: true)
Error:
In here,in second menu contains unwanted things as [Select,SelectAll,Copy] with [Paste].
How can i resolve this,thanks in advance
You should override canPerformAction in a UITextField subclass to disable the item you don't want, then assign each uitextfield you created to the subclass.
For example, disable paste menu item in uimenucontroller:
class CustomTextField: UITextField {
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
if action == "paste:" {
return false
}
return super.canPerformAction(action, withSender: sender)
}
}
Usage:
let message_Textfield = CustomTextField()
Now paste menu item will be disabled for message_Textfield
Hi Thanks for your answer,But i found solution as follows:
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool
{
if(MenuBool == true){
if action == Selector("Copy") || action == Selector("star") || action == Selector("info") || action == Selector("forward") || action == Selector("Delete")
{
print("UIMenuController====>CellMenu")
UIMenuController.sharedMenuController().menuVisible = false
return true
}
print("UIMenuController====>Defaultmenu1")
return false
}else if MenuBool == false
{
print("UIMenuController====>Defaultmenu2")
return false
}else{
print("UIMenuController====>DefaultmenuElse")
return false
}
}
In,this manner working fine.
:):):)

Resources