Skip to content

Instantly share code, notes, and snippets.

@anjnkmr
Created May 20, 2017 04:49
Show Gist options
  • Save anjnkmr/d6681a9b1aea4f56ee57d449a2f3ee43 to your computer and use it in GitHub Desktop.
Save anjnkmr/d6681a9b1aea4f56ee57d449a2f3ee43 to your computer and use it in GitHub Desktop.
Android Like Context Menu
import UIKit
class AndroContextMenuAppearance{
static var MAX_HEIGHT: CGFloat = 300;
static var WIDTH: CGFloat = 200;
}
class AndroContextMenu{
let screenSize = UIScreen.main.bounds;
var options = [AndroContextMenuItem]();
let contextMenuController = AndroContextMenuController();
let viewController: UIViewController!
var anchorPoint: CGPoint!;
var height: CGFloat = AndroContextMenuAppearance.MAX_HEIGHT;
var width: CGFloat = AndroContextMenuAppearance.WIDTH;
var convertedRect: CGRect!;
init(viewController: UIViewController, sourceView: UIView, parentView: UIView){
self.viewController = viewController;
convertedRect = parentView.convert(sourceView.frame, to: viewController.view);
populateAnchorPoint(convertedRect);
assert(self.viewController != nil, "viewController property should be assigned on constructor, so menu will be presented on that viewController");
prepare();
}
func populateAnchorPoint(_ rect: CGRect){
var x: CGFloat = rect.minX - width + 5;
var y: CGFloat = rect.maxY - 5;
//Setting x Coordinate
if x < 0{
x = rect.maxX - 5;
}
if x + width > screenSize.width{
x = rect.midX - width/2;
}
//Setting y Coordinate
if y + height > screenSize.height{
y = rect.minY - height + 5;
}
if y < 0{
y = rect.midY - height/2;
}
anchorPoint = CGPoint(x: x, y: y);
}
private func prepare(){
contextMenuController.menu = self;
contextMenuController.modalPresentationStyle = .overCurrentContext;
contextMenuController.modalTransitionStyle = .crossDissolve;
}
func show(){
height = CGFloat(options.count * 44);
if height > AndroContextMenuAppearance.MAX_HEIGHT{
height = AndroContextMenuAppearance.MAX_HEIGHT;
}
populateAnchorPoint(convertedRect);
viewController.present(contextMenuController, animated: true, completion: nil);
}
func addOption(_ option: AndroContextMenuItem){
options.append(option);
}
}
extension UIViewController{
func showAndroContextMenu(with options: [AndroContextMenuItem], sourceView: UIView, parentView: UIView){
let menu = AndroContextMenu(viewController: self, sourceView: sourceView, parentView: parentView);
for option in options{
menu.addOption(option);
}
menu.show();
}
}
class AndroContextMenuController: UIViewController {
let tableView = UITableView();
let button = UIButton();
var menu: AndroContextMenu!;
override func viewDidLoad() {
super.viewDidLoad();
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.3);
button.frame = self.view.bounds;
button.backgroundColor = UIColor.clear;
button.addTarget(self, action: #selector(AndroContextMenuController.dismissThis), for: .touchUpInside);
self.view.addSubview(button);
assert(menu != nil, "menu should not be null");
tableView.delegate = self;
tableView.dataSource = self;
tableView.frame.origin = menu.anchorPoint;
tableView.frame.size = CGSize(width: menu.width, height: menu.height);
tableView.separatorStyle = .none;
tableView.backgroundColor = UIColor.white;
tableView.layer.cornerRadius = 5;
tableView.clipsToBounds = true;
tableView.isScrollEnabled = AndroContextMenuAppearance.MAX_HEIGHT == menu.height;
self.view.addSubview(tableView);
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning();
}
func dismissThis(){
self.dismiss(animated: true, completion: nil);
}
}
extension AndroContextMenuController: UITableViewDelegate, UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return 1;
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menu.options.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell();
let menuItem = menu.options[indexPath.row];
cell.textLabel?.text = menuItem.title;
cell.textLabel?.textColor = menuItem.titleColor;
return cell;
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let menuItem = menu.options[indexPath.row];
menuItem.action?(menuItem);
self.dismissThis();
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 44;
}
}
class AndroContextMenuItem{
var title: String = "";
var action: ((AndroContextMenuItem)->())? = nil;
var titleColor: UIColor = UIColor.black;
init(title: String, action: ((AndroContextMenuItem)->())? = nil, titleColor: UIColor? = nil){
self.title = title;
self.action = action;
if titleColor != nil{
self.titleColor = titleColor!;
}
}
}
import UIKit
class ViewController: UIViewController {
@IBOutlet var buttons: [UIButton]!
var options = [AndroContextMenuItem]();
override func viewDidLoad() {
super.viewDidLoad()
for button in buttons{
button.addTarget(self, action: #selector(ViewController.contextMenuTapped(sender:)), for: .touchUpInside);
}
var values = ["Edit", "Post on Facebook", "Tweet", "Others", "Delete"];
var colors = [nil, nil, nil, nil, UIColor.red];
for i in 0..<values.count{
let val = values[i];
let col = colors[i];
let option = AndroContextMenuItem(title: val, action: { (menuItem) in
print("Option Selected: \(menuItem.title)");
}, titleColor: col);
options.append(option);
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func contextMenuTapped(sender: UIButton){
print("Button Tapped: \(sender)");
self.showAndroContextMenu(with: options, sourceView: sender, parentView: self.view);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment