How can I disable the touch events on two UIImageViews after i drag them into each other? - objective-c

I have two draggable UIImageViews within a UIView. When i drag one UIImageView over the other, i would like to disable touch events to those two UIImageViews and create another UIImageView which does respond to my touch events. Ive tried using setUserInteractionEnabled: but it isnt really doing anything at all. Forgive me if this is a no brainer but i am new to programming, here is what i have so far. Please give me some feedback on my code and give me some constructive criticism because i feel as though i am setting this up all wrong.
-(void)swapImageViews
{
if ((self.imgView1.center.x == self.imgView2.center.x) &&
(self.imgView1.center.y == self.imgView2.center.y)) {
[self addSubview:self.imgView3];
self.imgView3.center = CGPointMake(self.imgView1.center.x, self.imgView1.center.y);
[self.imgView1 removeFromSuperView];
[self.imgView2 removeFromSuperView];
[self.imgView1 setUserInteractionEnabled:NO];
[self.imgView2 setUserInteractionEnabled:NO];
}
}
So once again, the goal is to swap out two image views with a fresh one that i can also drag around.
The problem i am running into is that my touch events are still moving the first two image views around behind the third one.

You say that the third image appeared, but the the other two are still in the view and they still receive touch events. There are two two scenarios for this:
You've already added the third image view somewhere else, and your method is never called, or the condition is never met.
self.imgView1 and self.imgView2 are not set to the actual objects in your view stack. They're probably just nil. you can debug that by NSLog(#"%# %#", self.imgView1, self.imgView2); in that method, before the condition, or in viewWillAppear or viewDidLoad.
The reasons that support my opinion:
In that method, you call removeFromSuperview to the two image views you want to disable, yet they are still visible and receiving touch events. That means that this method is not executed. Which means they're nil or some other objects, that are different than the visible ones.
The condition in the start of the method is a bit strict. It's hard to be easily met. Yet the method is executed as the third image view is added to the view. This can happen when the object is nil, so the values returned by methods are the default values.
Check this link "the 10th item" and this one too.

Well this is not the best way to do that, but with the code that you provide to us, we have not a lot of options, you can do something like that, to remove the gestures:
-(void)swapImageViews{
if ((self.imgView1.center.x == self.imgView2.center.x) &&
(self.imgView1.center.y == self.imgView2.center.y)) {
[self addSubview:self.imgView3];
self.imgView3.center = CGPointMake(self.imgView1.center.x, self.imgView1.center.y);
[self.imgView1 removeFromSuperView];
[self.imgView2 removeFromSuperView];
NSArray* gestures1 = [self.imgView1.gestureRecognizers copy];
for(UIGestureRecognizer *gesture in gestures1){
[self.imgView1 removeGestureRecognizer:gesture];
}
NSArray* gestures2 = [self.imgView2.gestureRecognizers copy];
for(UIGestureRecognizer *gesture in gestures2){
[self.imgView2 removeGestureRecognizer:gesture];
}
}
}

Related

iOS7 Keyboard Behavior with SearchBar/UITableView: Offset on secondary view appearances

Problem
I am having a rather big issue with the iOS7 keyboard appearance. I have a Searchbar on a UIViewController with TableView Delegation/Data Source setup (I am using the self.searchDisplayController delegates as well). I segue from this scene to a prototype tableview to show the results.
Here is the issue:
On first load I can see the keyboard being displayed when I tap into the text field of the UISearchBar. I can type and perform a search with the results being shown in the next scene.
I've added NSNotifications to view the keyboard properties in local methods keyboardWillShow and keyboardWasShown. I can see on the first scene appearance (after the view is completely loaded):
I segue to the result tableview at this point and when I navigate back and touch the text field, my keyboard shows up either fully or partially off-screen:
When I look at the keyboardWillShow notification at this point I can see that my keyboard values are incorrect:
I've researched and tried many possibilities including:
Added the following to my main view controller:
-(BOOL)canResignFirstResponder
{
return YES;
}
-(BOOL)canBecomeFirstResponder
{
return YES;
}
Configured the following in my view did load
self.searchDisplayController.searchBar.spellCheckingType = UITextSpellCheckingTypeNo;
self.searchDisplayController.searchBar.autocapitalizationType= UITextAutocapitalizationTypeNone;
self.searchDisplayController.searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
self.searchDisplayController.searchBar.keyboardType = UIKeyboardTypeDefault;
Put in standard stubs for:
-(void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
I've noticed that if I choose a Partial Curl as my segue mode, the keyboard remains accessible when I roll back to the main view controller (but then it was never fully off screen in that case). However if I move from the results tableview to a detail scene and then navigate back to the main view controller, the keyboard appears off-screen again.
Question
Is there a method I can use to intercept the misplaced keyboard so that it displays in the default location?
NB: Along these lines, I have created a NSDictionary property to hold the initial userInfo values with the correct keyboard placement. I am not sure how to reassign these values to get the keyboard to return to it's original placement.
BTW - This seems a bit of a hack to get the keyboard fixed due to a bug in IB, is there some other way that I can try to remedy the situation?
Thanks in advance for any feedback!
Solution
This was such an obscure issue that I'm sharing the solution to save the next person some effort. Like most programming issues, it turns out this one was self-inflicted. In my original iteration of this project I had turned off rotational support as I am learning auto-layout and I wanted to ease into the transition from Springs and Struts. Somehow between the start of the project and the code release I ended up with this bit of code in the Main Scenes' View Controller.
//BAD
- (NSUInteger) supportedInterfaceOrientations
{
return !UIInterfaceOrientationPortraitUpsideDown;
}
instead of returning a valid enumeration like...
//OK
- (NSUInteger) supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}

Getting this warning "while a presentation is in progress" (Xcode)

In a project I'm writing I get this error when I present a new view controller:
Attempt to present.... while a presentation is in progress!
I think it happens because I first present a new view controller, and then in that view I present another view controller.
- (void)loadLabelSettings {
LabelSettingsViewController *labelSettings =
[[LabelSettingsViewController alloc] init];
labelSettings.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:labelSettings animated:YES completion:nil];
}
The program doesn't crash or anything it runs just fine, and there is no errors or warnings in my code. So my question is: Is it something I should be concerned with and if yes how do I solve it?
Thanks in advance :)
It is, like you said, probably caused by presenting two view controllers at the same time. Wait with presenting the second view controller until the first one has been fully presented. A good location would be to do this in viewDidAppear.
In my case, I connected a UIViewControllers UIButton with a second UIViewController by a UIStoryboardSegue. Inside my code a called it a second time programmatically. So pressing the UIButton caused presenting the specified view two times.
I figured out my problem, as Scott wrote it was because I was presenting 2 view controllers at the same time. It happened because I had a button that had a UILongPressGestureRecognizer, that showed the new view controller. The problem was that when using a UILongPressGestureRecognizer, the method that is being called, is called twice. First when the long press is detected and when your finger is released from the screen. So the presentViewController method of the same view, was called twice. I fixed this by only reacting to the first detection. Here is the code :
- (void)loadButtonSettings:(UILongPressGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
}
}

Can I use a customized checkmark with UITableView's allowsMultipleSelectionDuringEditing set to YES?

A picture's worth a thousand words...
For a bit more background, I have a UITableView leveraging iOS 5's allowsMultipleSelectionDuringEditing set to YES. This results in the empty and filled edit controls being shown on the left of the cell any time the cell is in edit mode. This behavior is exactly what I want. I just want to change the appearance of these check marks.
I know it would be possible to write custom selection logic and basically roll my own version (like this and this), but that's what I want to avoid. The system is already in place, and I want to re-use as much of it as possible.
This is the closest I've come. It's simple and it works, while reusing almost all of the pre-baked system. It's also a giant hack however, and relies on exploiting the undocumented view hierarchy of UITableViewCell after a little runtime introspection.
In a nutshell, this simply hides the view normally responsible for showing the checkmark, allowing me to add my own view that can be shown in its place. I can then manipulate this stand-in view when the cell's selection or editing state changes...
To prevent the standard checkmark from appearing, all that's needed is a custom -layoutSubviews implementation. It's called, per the documentation, after both -willTransitionToState: and -setEditing:animated:, ensuring the state is always valid when either isSelected or isEditing changes.
- (void)layoutSubviews
{
[super layoutSubviews];
// Find the offending view, and quietly bury it...
for (UIView* subview in [self subviews])
{
// As determined by NSLogging every subview's class, and guessing which was the one I wanted
if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellEditControl"])
{
[subview setHidden:YES];
}
}
if ([self isEditing])
{
// Show the custom view however you want.
// The value of [self isSelected] will be useful...
}
else
{
// Hide the custom view.
}
}
I would still welcome a solution that's a bit more... kosher.

How do I know if a view is visible or not?

Say I have two view controllers: xVC and yVC. I have used the shake API and and have used the methods -(void)motionBegan,-(void)motionEnded: and -(void)motionCancelled in xVC. What happens is when the device is shaken, it fires a simple animation. Now the thing is that this animation is fired even when the I have yVC open that is, when yVS.view has been added as the subview. What I am looking for is some if condition which I can use in -(void)motionEnded: like this:
if(yVC == nil)
{
//trigger animation
}
By that I mean that the shake shouldn't work when yVC is visible. How do I do that? Please help.
The general advice I have seen and used is to ask a view if it has a non-nil window property:
if( ! yVC.view.window) {
// trigger animation
}
But note that this doesn't always equate with being visible; though in most apps it's about as good as you can performantly get (the basic case where it's not accurate is when a different view completely obscures it, but this may still satisfy your needs)
Add this to both of your view controllers:
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
visible = YES;
}
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
visible = NO;
}
Now, just check the variable isVisible of both the view controllers and trigger your animation likewise.
The previous answers all work to some degree, but fail to take modally presented view controllers into account. If view controller A presents view controller B, of the previous answers will tell you that A is still visible. If you, like me, want to know whether or not the view is actually visible (and not just a part of the view hierarchy), I would suggest also checking the presentedViewController property:
if (self.isViewLoaded && [self.view window] && !self.presentedViewController) {
// User is looking at this view and nothing else
}
This works since presentedViewController will be non-nil whenever the current view controller OR any of its ancestors are currently presenting another view controller.

Cocoa NSView subview blocking drag/drop

I have an NSView subclass which registers for drag files in init method like this:
[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
The drag drop works perfectly fine, but if I add a subview to this view with the exact same frame, it doesn't work any more. My guess is that the subview is block the drag event to go to super view. Can can I avoid that? Thanks
Also, I know I am asking two questions, but I don't want to create a new topic just for this: When I am dragging, my cursor doesn't change to the "+" sign like with other drags, how can I do that?
Thanks again.
UPDATE:
Here's the how I have it set up in my IB:
The DrawView is the custom class I was talking about that registered for draggedtypes. And the Image view simply is a subview, I dragged an image from the media section...
If it helps, here's my relevant code for DragView:
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
return NSDragOperationCopy;
}
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
NSPasteboard *pboard;
pboard = [sender draggingPasteboard];
NSArray *list = [pboard propertyListForType:NSFilenamesPboardType];
if ([list count] == 1) {
BOOL isDirectory = NO;
NSString *fileName = [list objectAtIndex:0];
[[NSFileManager defaultManager] fileExistsAtPath:fileName
isDirectory: &isDirectory];
if (isDirectory) {
NSLog(#"AHH YEA");
} else {
NSLog(#"NOO");
}
}
return YES;
}
The answer to the second part of your question is this:
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender{
return NSDragOperationCopy;
}
if you return NSDragOperationCopy, you get the mouse badge for a copy operation. (You can and should, of course, not just return NSDragOperationCopy unconditionally; check the objects on the pasteboard to see if you can accept them.)
I'm not sure what the answer to the first part of your question is, because I'm unable to recreate the subview blocking effect.
Okay, the answer is unfortunately that you can't. The image you dragged is contained in an NSImageView, and NSImageViews accept drag events themselves, so it's grabbing the drag event and not doing anything with it. If your subview was a custom class, you could either a) not implement drag and drop, in which case the drags would be passed through; b) implement drag and drop to accept drags for the subview. In this case, you're using a class over which you don't have any control. If all you want it to do is display an image, you should be able to make another NSView subclass that does nothing but draw the image in drawRect:
As mentioned in the comments, NSImageViews have their own drag and drop enabled by default (used for accepting images that are dragged onto the NSImageView). If you don't want to use this behavior and instead want to use the super view's drag and drop behavior, you'll want to unregister the dragging behavior in the NSImageView.
Objc:
[imageView unregisterDraggedTypes];
Swift:
imageView.unregisterDraggedTypes()
(in case anyone else stumbled across this question first and not the linked one in the comments)