UIButton inside a UIView not working after UIView is animated - objective-c

I have seen many questions in this forum which gives answer to this topic "UIButton inside a UIView, when animated doesn't work", but after having tried out the answers like
a) UIViewAnimationOptionAllowUserInteraction to the options
b) subView.setUserInteractionEnabled = YES
c) [button addTarget:self action:#selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside];
none of them is working for me :-(
Here is the scenario. App is in landscape mode and a UIView called menuView is placed at x=480,y=0. Its height=200 and width=150. So, when I tap the button on the top right corner, the following code is executed
- (IBAction)showMenu {
[menuView setUserInteractionEnabled:YES];
[optionsButton setUserInteractionEnabled:YES];
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationCurveEaseOut | UIViewAnimationOptionAllowUserInteraction
animations:^{
self.view.frame = CGRectOffset(self.view.frame, 0, 200);
}
completion:NULL];
}
Output?: View is appearing, but optionsButton inside the menuView is not clickable. I hooked up to an event, did an NSLog(#"Options button clicked"), but nothing happens :-(
Please tell me what I am doing wrong?
Update: When I place the subview that is "menuView" inside the self.view, then upon running and click the optionsButton, I get the NSLog message. Only if I specify the origin.x of menuView to be 480 and above, it doesn't work.

If you can see the UIButton, its userInteractionEnabled (YES by default!) is set, and its superview's userInteractionEnabled in the whole hierarchy is YES - you should be able to interact with it.
An animation will never change your userInteractionEnabled property! So what you are doing there is simply unnecessary. If you try to enable interaction during animation - that's a different story and is just an option passed to the animation message.
So if that's not your problem (and is probably not), then I guess one of the superview's frame is cropping the UIButton!
Now you see, if a UIButton (or any UIView) is outside a UIView's frame, it can still be visible, unless clipsToBounds is set on the superview.
And the outcome of that situation is: You can see me, but you can't touch me.

In my case missing a constraint on the height of the containing view caused the view height to be 0. Buttons were visible, below the view, but untouchable.

For me it was all about an overlooked mistake in the animation code itself. Where my new height was being called I was actually calling my new width, and where my new width was being called I was actually calling my new height, therefor causing the UIView to be super long yet super narrow, thus pushing all content within the center of view, outside of the view reach.
Change your UIViews background color to Red so that you can actually see where it animates to.
If your UIView is within another view, turn on clipSubviews so that you only see what is actually enabled within the subView.
Run your project.
If your UIView is not its normal height and width, more than likely you have overlooked some code in your animation call.
Make sure that your x, y, width, height are being shown in the correct orders within the animation code.

In my case, it seems like my animated transitioning is not yet completed. I forgot to to put completeTransition:Bool at the end of my animateTransition:context method.

Related

UIScrollView not scrolling when adding item in IB and code

When I create a Scrollview on my scene, and then add a button to scene in IB. Then I go into the code, set the content size, enable user interaction and add another button. When I run the program in the simulator the Scrollview does not work, if I remove the button that is in IB on the scene it works just fine. Is it not possible to add items to the scrollview both in IB and programmatically?
EDIT: I thought it may be something in the app I already had. So I decided that I would create anew project and all it has in it is code, and the scene picture below. It is indeed added below the ScrollView.
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[myButton setTitle:#"My Button" forState:UIControlStateNormal];
myButton.frame = CGRectMake(100, 100, 150, 50);
[scrollView addSubview:myButton];
scrollView.userInteractionEnabled = YES;
[scrollView setContentSize:CGSizeMake(320, 1000)];
Here your scrollView is not scrolling due to the autoLayout, uncheck the auto Layout if you are not using.
I Just made a similar to your requirement. It is working fine, and after allowing autoLayout it just stopped scrolling.
The auto layout constraints fits to the visible part of the screen, if the objects in scrollView are more then screen size, it will scroll.
So my suggestion if you are not using autoLayout just uncheck it, and works fine.
Here is an helpful link: http://developer.apple.com/library/ios/#releasenotes/General/RN-iOSSDK-6_0/_index.html#//apple_ref/doc/uid/TP40012166-CH1-SW19
Basically what this says is that, with auto-layout you don't and shouldn't have to use setContentSize:. Instead your inner view should have it's edges snapped to the edges of the scrollview. Let me demonstrate how I solved it.
scrollBackground : a view that contains every other view that needs to scroll in the scrollview.
[scrollview addSubview:scrollBackground];
[scrollview addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[scrollBackground(1000.0f)]|"
options:0 metrics:nil
views:NSDictionaryOfVariableBindings(scrollBackground)]];
[scrollview addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|[scrollBackground(==scrollview)]|"
options:0 metrics:nil
views:NSDictionaryOfVariableBindings(scrollBackground)]];
The key here being those | at beginning and end of the VisualFormat String. These will tell the scrollview what is the contentSize. Of course if you have a contentSize smaller than the frame size of the scrollview it won't scroll in that direction. In my example this is true for width. It only scrolls up and down.
Make sure you set your content size in viewDidLoad or at some point after the view has already been loaded from the nib file.
As vishy Pointed out, your Button should be part of the ScrollView's Hierarchy, else you'll just be scrolling an empty view.
In the case you posted, the scrollview will not scroll because all of the content is visible. Try changing that last line to:
[scrollView setContentSize:CGSizeMake(50, 50)];
and it will start scrolling, because the content size is smaller than all of the content in the view.
check d2burke answer in this question
you should place your code in viewWillLayoutSubviews instead of viewDidAppear

How to dynamically reposition UIButton as subview of UIImageView when rotating

I'm working on an iPad app that lets you control different things in a prototype of an intelligent house. For example it lets you turn lights on and off. For this, I have made a UIImageView that shows the floor plan of the house and added UIButtons as subviews for each lamp that can be toggled.
As you can see the buttons are placed perfectly on the floor plan using the setFrame method of each UIButton. However, when I rotate the iPad to portrait orientation, the following happens:
The buttons obviously still have the same origin, however it is not relative to the repositioning of the image.
The floor plan image has the following settings for struts and springs:
and has its content mode set to Aspect Fit.
My question is
how do I dynamically reposition each UIButton, such that it has the same relative position. I figure I have to handle this in the {did/should}AutorotateToInterfaceOrientation delegate method.
It should be noted that the UIImageView is zoomable and to handle this I have implemented the scrollViewDidZoom delegate method as follows:
for (UIView *button in _floorPlanImage.subviews) {
CGRect oldFrame = button.frame;
[button.layer setAnchorPoint:CGPointMake(0.5, 1)];
button.frame = oldFrame;
button.transform = CGAffineTransformMakeScale(1.0/scrollView.zoomScale, 1.0/scrollView.zoomScale);
}
Thank you in advance!
I find the best way to layout subviews is in the - (void) layoutSubviews method. You will have to subclass your UIImageView and override the method.
This method will automatically get called whenever your frame changes and also gets called the first time your view gets presented.
If you put all your layout code in this method, it prevents layout fragmentation and repetition, keeps your view code in your views, and most things just work by default.

UIView animation does not allow center setting

I try to move an ImageView (say, view1) so that its center matches another ImageView's center (view2), while resizing it. On completion this first view is removed, but is image is copied into the view2, so its image seems to remain.
For that purpose I tried many combinations but with any, even creating a new ImageView3 that I can insert into view2, as soon as I try to set the center of view3 to match the center of view2, nothing happens and my image disappears, never being inserted in view2. Without setting the center, everything works. Damn, Why? Did I miss something obvious or am I stupid?
Or should I definitely use coreAnimation?
[UIView animateWithDuration:0.5 animations:^{
view1.center = view2.center;
view3.center = view2.center;
} completion:^(BOOL finished){
[view2 addSubview:view3];
}];
In the completion block, you add view3 as a subview to view2. This is probably not what you want, at least not without further adjusting its position. Since you have reset its center in the animation, the coordinates are based on the parent view. When you add it to view2, the apparent position shifts based on the relative position of view2.

How to create a "stretchable" UIView

I have a UIView that contains another UIView. The outer UIView draws a border around the inner UIView via drawRect. (The border is too complicated to be drawn via CALayer properties.)
At present, when I animate the resizing of the outer UIView, its drawRect method is called once at the beginning of the animation and the result is stretched or shrunk. This does not look good.
I am looking for a way to either redraw the content at every step of the animation, or find a way to achieve the same visual effect. (The result should be similar to the resizing of a stretchable UIImage.)
You should change view's content type to:
your_view.contentMode = UIViewContentModeRedraw;
And it will redraw each time its frame changes.
I ended up adding subviews with autoresizing masks that kept them positioned correctly during the animation.
You need to send a [UIView setNeedsToDisplay] to the view for every time the frame size is changed, you could try overriding the setFrame: method like
- (void)setFrame:(CGRect)r
{
[super setFrame:r];
[self setNeedsToDisplay];
}

Animate the change of a stroke color on UIView

I'd like to "pulse" the stroke color on a path that I've drawn in a UIView's drawRect. But not sure this is possible?
- (void)highlightView {
if (!isHighlighted) {
[UIView beginAnimations:NULL context:nil];
[UIView setAnimationDuration:0.75];
self.strokeColor = kHighlightColor;
self.strokeWidth = 2.0;
[UIView commitAnimations];
self.isHighlighted = YES;
}
}
I'm only able to see the change if i setNeedsDisplay on the view. But that bypasses the animation. I can think of a few workarounds if this isn't possible like overlaying another semi-transparent view with the proper color and just fading it in... but for some reason I thought animating color properties was possible in Cocoa?!? Perhaps I'm mistaken. Hopefully one of you can set me straight.
You absolutely can animate colour properties with UIView, but it doesn't really make sense in the context you're dealing with right now.
In drawRect: you are essentially painting a colour when you stroke a path, those bits just get blitted to the screen. At that point, the colour isn't really a property of the view so much as part of the painting.
I wrote a UIView that pulsed this summer (emulating the "In Call" status bar) by creating two UIViews stack atop each other, and animating both of their color properties.