UIContextMenuInteraction for UIControl [duplicate] - ios

This question already has answers here:
iOS 14 Context Menu from UIView (Not from UIButton or UIBarButtonItem)
(2 answers)
Closed 1 year ago.
I know UIButton has a menu property so that context menus can be added to the button. But for other UIControl subclasses, such as custom UIControls, this property does not exist.
I know that one way of adding a context menu to a UIControl is to call addInteraction and then adopt the delegate for the context menu. But then if I have multiple controls on the page, how do I add the menus for all the different controls since they all share the same delegate?
Alternatively, how could I add a menu property to my UIControl subclass? I know that UIControl has a property called contextMenuInteraction and that is apparently automatically populated, but I do not understand how to use that property. I know the control has to implement the delegate method (UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location, but then inside that method, do I just construct the UIContextMenuInteraction object? And when the menu property is set, how do I get it to call the delegate method?
Essentially I would like to be able to imitate the UIButton class's menu property in my UIControl subclass. But if not that, then I would like to at least figure out how to support multiple context menus all sharing the same delegate.

You can create and add a new UIContextMenuInteraction object to views (or controls, etc) in the same way you add a new gesture recognizer.
Then, in your menu action handler, you can get the view that was long-pressed to show the menu via the .sender:
UIAction *someAction = [UIAction actionWithTitle:#"Some Menu Option"
image:nil
identifier:nil
handler:^(__kindof UIAction* _Nonnull action) {
// get the view that presented the context menu
UIView *v = ((UIContextMenuInteraction *)(action.sender)).view;
// do something
}];
So, if I have a custom UIControl called MySwitch, and I want to add 7 of them to a stack view, giving each a context menu, I could do something like this:
for (int i = 1; i < 8; i++) {
MySwitch *v = [MySwitch new];
[v setTitle:[NSString stringWithFormat:#"Switch: %d", i]];
[v.heightAnchor constraintEqualToConstant:60.0].active = YES;
UIContextMenuInteraction *interaction = [[UIContextMenuInteraction alloc] initWithDelegate:self];
[v addInteraction:interaction];
[stack addArrangedSubview:v];
}

Related

ios-Update multiple UIViews on UIScrollview

I have a ViewController that contain a UIScrollView, that contains a few views which are custom UIViews (very simple uiimage + uibutton) that the user can scroll between (one custom view at the a time).
I want the user to be able to "mark" a photo, and then to display a certain text, when the user select a different photo (by using the button), I want to update the text on the previous selected photo and the current selected.
What should I do?
Inside the view itself I don't have access to the previous view, I figured I should send a notification to the view controller but than I have no access to the button I want to update
You could use tags to identify the scrollViews subviews.
Add a tag to the subview and the same tag to the button that belongs to the subview while adding them to the scrollView.
view.tag = 1;
button.tag = 1;
Then in the method called by the buttons, you can use the buttons tag to get the according custom view.
-(void)buttonClick:(id)sender{
UIButton *btn = (UIButton*)sender;
NSInteger tag = btn.tag;
UIView* customView = [self.scrollView viewWithTag:tag];
}
To access the prevoius selected view, store the tag of that view when editing for the next call of the function.

Set action for custom UIBarButtonItem

I've created a UIView that has to be added to BarButtonItem. I can't set action to this button though. Here's the code I am using:
self.menuBarButton = [[UIBarButtonItem alloc] initWithCustomView:self.containerView];
[self.menuBarButton setTarget:self];
[self.menuBarButton setAction:#selector(menuButtonTapped)];
self.navigationItem.leftBarButtonItem = self.menuBarButton;
The action doesn't run with success. I also tried to set an action directly to bar button after it's set to custom.
[self.navigationItem.leftBarButtonItem setTarget:self];
[self.navigationItem.leftBarButtonItem setAction:#selector(menuButtonTapped)];
When I do it for the usual barbutton item it works. Could you help spotting what's wrong?
A UIBarButtonItem initialized with a custom view expects the view to handle user interaction:
The bar button item created by this method does not call the action method of its target in response to user interactions. Instead, the bar button item expects the specified custom view to handle any user interactions and provide an appropriate response.
If your UIView doesn't handle any user interaction yet, then as Miken says, you can replace the UIView with a UIButton that you've configured with addTarget:selector.
In particular, if you're trying to set an image, UIButton can do that via setImage:forState:.
See also this answer.
When you use a custom view for a UIBarButtonItem, then you have to handle the actions yourself. Replace your custom view with a UIButton, or add a UIButton to your custom view, and then add a target & selector to the button.
Here's an example but keep in mind I'm free-forming this example, so it may not be 100% accurate code but the concept will be the same.
UIButton *button = [UIButton buttonWithType:UIBarButtonTypeCustom];
UIBarButtonItem *bbi = [[UIBarButtonItem alloc] initWithCustomView:button];
[button addTarget:self selector:#selector(myBtnClk)];
To add action to a button you need two things: create a method, then add it to the button. Here is the method (pay attention to the syntax) :
- (IBAction) my_method : (id) sender {
UIButton * btn = sender;
}
Now the action assignment (where the button was created) :
[ my_button addTarget : self
action : #selector( my_method : )
forControlEvents : UIControlEventTouchUpInside ];
Instead of using - setAction alone, I recommend using: - addTarget:action:forControlEvents:. In iOS (AppKit) there are many type of events. You can take a look at the attached image for reference. Typically Touch up inside is appropriate for button press down.
I decided this problem. All answers was about adding target and action to UIButton and then create a UIBarButtonItem with this button. But as I said it's really necessary to set UIView to this button(I've forgotten to say it's necessary because this UIView has animation, maybe that's why all answers was good but wrong for my situation.)
But one answer(https://stackoverflow.com/a/28054357/4464891) from Jason Owen gives me great idea. I have to use my custom view, so I've set selector to my custom UIView by adding UITapGestureRecognizer with initWithTarget:action::
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(menuButtonTapped)];
[self.containerView addGestureRecognizer:tapRecognizer];
And when I click button tapRecognizer runs my selector. So that's it. Hope it'll be helpful for someone=)

Automatic view id's like in android

I need to write some gui for IOS, yet i am so used to the automatic view id's that android has, that i am at a loss when it comes to identifying views without putting explicit tags in the interface builder
is there some way to automatically identify iOS views from interface builder or otherwise ?
maybe a library that does that ?
Thats why you have IBOutlets. Create an IBOutlet for your views, like so IBOutlet UIView *view1; etc etc then in link the IBOutlet to your view in Interface Builder. Now you can progammatically use the view1 variable which will modify your view associated to that IBOutlet in interface builder\
UPDATE
Progammatically create all your views like so:
for(int i = 0; i < 20; i++){
//set your x and y coordinates with some awesome maths, maybe you want to create a grid, so update the x coordinate accordingly
UIView *view = [UIView alloc] initWithFrame:CGRectMake(whateverframe)];
UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(viewTapped:)];
[imageView addGestureRecognizer:singleTapGesture];
[singleTapGesture release]; //remove line if you have an ARC project
view.tag = i; //this is just for you since you like to handle your views with id tags
}
Then your one method that will handle the taps for all your code
- (void) viewTapped:(UIGestureRecognizer*)recognizer
{
UIView *viewTapped = recognizer.view; // This is the view associated with gesture recognizer.
int id = viewTapped.tag; //heres the tag id do whatever you like
//do something with either that view variable which will return the view that was tapped
//maybe you wanted to change the color of it or something or change the contents of it
//or you can do something with the id tag of that view.
//or maybe you just want to handle it with if else statements like so
//if(viewTapped.tag == 1) //do this
//elseif(viewTapped.tag == 2) // etc etc
}
Set Tag for each and every view using property setTag and retrieve the corresponding view using – viewWithTag:. [abaseView subViews] returns an array of subviews. Filter the subviews using Class and Tag.

ios adding custom inputView

I'm stuck on a concept in iOS that I can't seem to understand, no matter how much I read about it. I'm trying to override the standard iOS number pad with a custom design. When the user touches the UITextField, I want the custom inputView to reveal instead of the standard number pad.
I created an separate .h/.m/.xib ViewController class for my custom inputView called "customInputViewController" Right now, it's just a dark background and one button that obscures about half of the screen when the UITextField is touched (similar to the number pad, but it just looks different). My implementation fails when I click the one button in my custom inputView -- iOS throws an EXC_BAD_ACCESS error.
This is how I load the .xib file at runtime and attach the custom inputView to the UITextField object:
UIViewController *v = [[customInputViewController alloc] initWithNibName:#"customInputDesign" bundle:nil];
myTextInput.inputView = v.view;
In the .xib file of the custom inputView, I set the File's Owner to be "customInputViewController" and I created an (IBAction) method and attached it to a UIButton. When that button is clicked, the (IBAction) is set up to send an NSLog(#"Button Clicked") message. Nothing special. It's just a simple boilerplate implementation that continues to throw an error.
Maybe I'm doing this entirely wrong. Can anyone provide a simple example?
The view v.view is retained as the inputView property is defined as (readwrite, retain). However, if you release your customInputViewController v somewhere before the input button is clicked, you will get a crash (EXC_BAD_ACCESS)
You can try this in your main controller:
- (IBAction) keyboardButtonClicked
{
NSLog(#"keyboard Button Clicked");
}
- (void) viewDidLoad
{
// do your stuff here ...
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 100)]; // add autorelease if you don't use ARC
v.backgroundColor = [UIColor darkGrayColor];
UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom];
[b setTitle:#"Test button" forState:UIControlStateNormal];
[b addTarget:self action:#selector(keyboardButtonClicked) forControlEvents:UIControlEventTouchUpInside];
b.frame = CGRectMake(80, 25, 160, 50);
[v addSubview:b];
myTextInput.inputView = v;
}
Should work fine ...
First of all, Take a look at this
The UIKit framework includes support for custom input views and input
accessory views. Your application can substitute its own input view
for the system keyboard when users edit text or other forms of data in
a view. For example, an application could use a custom input view to
enter characters from a runic alphabet. You may also attach an input
accessory view to the system keyboard or to a custom input view; this
accessory view runs along the top of the main input view and can
contain, for example, controls that affect the text in some way or
labels that display some information about the text.
To get this feature if your application is using UITextView and
UITextField objects for text editing, simply assign custom views to
the inputView and inputAccessoryView properties. Those custom views
are shown when the text object becomes first responder...
Actually i don't need to mention all this mess to you, but there is an interesting reason for mentioning this, from the first sentence i am mentioning view-view-view, but you are making the input view in a separate view controller and you are trying to assign it as an input view of your textfield and init shouldn't be creating the view, loadView does that. Calling the view getter (v.view) when view is nil will cause loadView to be invoked.Thats why it is crashing with EXC_BAD_ACCESS.
Source : Text, Web, and Editing Programming Guide for iOS

ios custom navigation (vertical) advice

I have an idea for an ios5 navigation I'm doing on an app and I thought it wise to get some constructive criticism from SOF about my idea.
Idea:
UIView containing 6 or so buttons stacked vertically
UIButtons have a selected state.
Buttons static/global keeps track of last touched button and always resets the last touched button when a new UIButton is touched.
Question:
Can you read and access the children of the UIView?
eg. (pseudocode)
for (i in [myView children]) {
[[myView getChildAt:i] doSomethingToThisButton];
}
Thanks all!
Yes. Here's the non-pseudocode (well, mostly):
for (UIView *subview in [myView subviews]) {
[subview doSomethingToThisButton];
}
Or, if you prefer
for (int i = 0; i < [myView.subviews count]; i++) {
[[myView.subviews objectAtIndex:i] doSomethingToThisButton];
}
Don't make your last touched button a static variable because then you can only have one such control in your whole app. Make a UIView subclass to act as the container for your buttons, and have the last selected view be a property of that class.
You may also want to make your containing view a subclass of UIControl instead of UIView, then you can make it send events and bind to it using drag and drop in interface builder, just like a regular control (e.g. a button).

Resources