UIScrollView adding two unwanted UIImageViews on touch out of nowhere - ios

I have noticed some strange behaviour for a UIScrollView.
In a new XCode(4.6.2 on OS X 10.8.4) iOS project, I've only added a RootViewController, with one line of custom code: adding a UIScrollView as a subview
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.view addSubview:[[UIScrollView alloc] initWithFrame:self.view.bounds]];
}
Then after touching the screen once, when I inspect the view hierarchy in lldb, the output is as follows.
(lldb) po [[UIWindow keyWindow] recursiveDescription]
$0 = 0x07126f10 <UIWindow: 0x7528660; frame = (0 0; 320 568); layer = <UIWindowLayer: 0x7528730>>
| <UIView: 0x7668580; frame = (0 20; 320 548); autoresize = W+H; layer = <CALayer: 0x7668910>>
| | <UIScrollView: 0x7666200; frame = (0 0; 320 548); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x7666df0>; layer = <CALayer: 0x76624f0>; contentOffset: {0, 0}>
| | | <UIImageView: 0x11121a30; frame = (313 541; 7 7); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x11121b60>> - (null)
| | | <UIImageView: 0x11121bd0; frame = (313 541; 7 7); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x11121c70>> - (null)
Suddenly the UIScrollView has two subviews which I did not add myself. This happens both in the simulator and on the device (also both iPhone and iPad).
So my question is:
Is this normal behaviour? If so, why would it be?

They are the scroll indicators. One UIImageView for the horizontal indicator and another one for the vertical indicator.

It's probably the scroll indicators. These details shouldn't really matter, since you should be using the scrollview's .contentView to hold your own subviews.

Related

subview does not present buttons or imageView

The view that is presented as a subview of another view has the following setup in a .Xib file.
Here is how the view is presented when the button to present it is touched.
Here is the code for how the view is presented.
-(void)setUpImageSelectorView{
ImageSelectorViewViewController *imageSelectorVC = [[ImageSelectorViewViewController alloc]initWithNibName:#"ImageSelectorViewViewController" bundle:nil];
imageSelectorVC.delegate = self;
imageSelectorVC.view.contentMode = UIViewContentModeScaleAspectFit;
imageSelectorVC.view.clipsToBounds = YES;
imageSelectorVC.imageView.image = img;
[self.view addSubview:imageSelectorVC.view];
}
Two issues:
This is not the correct code to add a subview that has its own controller. You should use custom container calls, e.g.:
-(void)setUpImageSelectorView{
ImageSelectorViewViewController *imageSelectorVC = [[ImageSelectorViewViewController alloc]initWithNibName:#"ImageSelectorViewViewController" bundle:nil];
[self addChildViewController:imageSelectorVC];
imageSelectorVC.delegate = self;
imageSelectorVC.view.contentMode = UIViewContentModeScaleAspectFit;
imageSelectorVC.view.clipsToBounds = YES;
imageSelectorVC.imageView.image = img;
[self.view addSubview:imageSelectorVC.view];
// you probably should set the `frame` of this view, too
[imageSelectorVC didMoveToParentViewController:self];
}
Note the addChildViewController and didMoveToParentViewController calls.
When you go to remove it, first call [imageSelectorVC willMoveToParentViewController:nil] and when when you're done removing everything, call [self removeChildViewController: imageSelectorVC].
See Creating Custom Container View Controllers in the View Controller Programming Guide for iOS. For a discussion of why all of this is so important (namely, keeping your view hierarchy synchronized with your view controller hierarchy) see WWDC 2011 video Implementing UIViewController Containment.
If you run the app through the debugger and pause the execution (hit the pause button, not the stop button), you can confirm the layout of the views by typing
(lldb) po [[UIWindow keyWindow] recursiveDescription]
<UIWindow: 0x7fd91b542420; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x7fd91b543000>; layer = <UIWindowLayer: 0x7fd91b5412d0>>
| <UIView: 0x7fd91b5442d0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fd91b5435c0>>
| | <_UILayoutGuide: 0x7fd91b5445e0; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x7fd91b537030>>
| | <_UILayoutGuide: 0x7fd91b544f40; frame = (0 667; 0 0); hidden = YES; layer = <CALayer: 0x7fd91b543850>>
| | <UIButton: 0x7fd91b446d50; frame = (5 285; 70 30); opaque = NO; tag = 12221; layer = <CALayer: 0x7fd91b4d8fb0>>
| | | <UIImageView: 0x7fd91b57aa20; frame = (0 0; 70 30); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7fd91b53b6f0>>
| | | <UIImageView: 0x7fd91b51c040; frame = (0 0; 0 0); clipsToBounds = YES; hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7fd91b539670>>
Sometimes views are there, but you won't see it if their width/height is zero (especially if you are clipping to the bounds).

NSLayoutConstraints issue

I've disabled autolayout from xib. But I'm facing this issue. Even I'm not adding any subview programmatically, and I'm having 3 element in this view:
1. UIImageView 2. UIButton 3. UIButton.
I'm suffering for the last 3 days but I'm not able to find out the error. So please any one help me. Your help will be appreciated.
NSLog(#"Constratins %#",[self.view constraints]);
Constratins (
)
View hierarchy unprepared for constraint.
Constraint: <NSAutoresizingMaskLayoutConstraint:0x294d9de0 h=-&- v=-&- UIViewControllerWrapperView:0x2d91c410.midY == UINavigationTransitionView:0xdd2aa40.midY>
Container hierarchy:
<UINavigationTransitionView: 0xdd2aa40; frame = (0 0; 1024 768); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0xdd2aca0>>
| <UIViewControllerWrapperView: 0x2a72b4d0; frame = (0 0; 1024 768); layer = <CALayer: 0x2a72ab40>>
| | <UIView: 0x2d869b30; frame = (0 0; 1024 768); gestureRecognizers = <NSArray: 0x2d8852e0>; layer = <CALayer: 0x2d86b510>>
| | | <MyImageView: 0x2d891040; baseClass = UIImageView; frame = (0 0; 1024 748); userInteractionEnabled = NO; layer = <CALayer: 0x2d8699f0>> - (null)
| | | <UIButton: 0x2d88c460; frame = (968 14; 40 40); hidden = YES; opaque = NO; layer = <CALayer: 0x2d88c550>>
| | | <UIButton: 0x2d88a220; frame = (963 9; 40 44); opaque = NO; layer = <CALayer: 0x2d88a310>>
View not found in container hierarchy: <UIViewControllerWrapperView: 0x2d91c410; frame = (0 0; 1024 768); autoresize = W+H; layer = <CALayer: 0x2d91c490>>
That view's superview: NO SUPERVIEW
Yes, I found the solution. The reason is : not setting the frame of UIViewControllerWrapperView
So i've do this
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.tabBarController.view.superview.frame = self.view.bounds;
}

UITapGestureRecognizer not working in first tab

This is so bizarre that I created a new project to test if I was going mad.
You can see the project here https://github.com/ojfoggin/TapTest
I've created a project with a UITabBarController As the initial view.
The first controller tab has two UIimageViews. Each image view has a UItapGestureRecognizer on it with an action that just logs "Tap 1" or "Tap 2".
If the TabBarController isn't used then everything works fine. However if the TabBarController is used then only the first tap recognizer works, the second doesn't.
However, if you switch t a different tab and then back again then both recognisers work?!?!?
Also, I have tried adding the Tao gesture recognisers in code and exactly the same thing happens.
Can anyone explain why this is happening and how to fix it?
TL;DR
Open your storyboard file as source code (xml) and replace
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
with
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES/>
For the view controller with the images.
Explanation
The problem is pretty easy to find with common debugging tools:
(lldb) po [[[[UIApplication sharedApplication] delegate] window] recursiveDescription]
shows
<UIWindow: 0x8c6a140; frame = (0 0; 320 480); autoresize = W+H; gestureRecognizers = <NSArray: 0x8c6a820>; layer = <UIWindowLayer: 0x8c66db0>>
| <UILayoutContainerView: 0x8c6a320; frame = (0 0; 320 480); transform = [0, -1, 1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0x8c62350>>
| | <UITransitionView: 0x8c6a880; frame = (0 0; 480 320); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x8c50240>>
| | | <UIViewControllerWrapperView: 0x8c6cd70; frame = (0 0; 320 480); autoresize = RM+BM; layer = <CALayer: 0x8c6c840>>
| | | | <UIView: 0x8c51070; frame = (0 0; 480 271); clipsToBounds = YES; autoresize = RM+BM; autoresizesSubviews = NO; layer = <CALayer: 0x8c51d80>>
| | | | | <UIImageView: 0x8c6bdc0; frame = (20 0; 219 160); autoresize = W+H; gestureRecognizers = <NSArray: 0x8c65180>; layer = <CALayer: 0x8c4c930>>
| | | | | <UIImageView: 0x8c6a780; frame = (247 0; 219 160); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x8c62a70>; layer = <CALayer: 0x8c6a9a0>>
| | | | | <_UILayoutGuide: 0x8c6c340; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x8c51110>>
| | | | | <_UILayoutGuide: 0x8c6c760; frame = (0 271; 0 0); hidden = YES; layer = <CALayer: 0x8c6c7d0>>
| | <UITabBar: 0x8c64180; frame = (0 271; 480 49); autoresize = W+TM; layer = <CALayer: 0x8c642a0>>
| | | <_UITabBarBackgroundView: 0x8a48e50; frame = (0 0; 480 49); autoresize = W; userInteractionEnabled = NO; layer = <CALayer: 0x8a48f40>>
| | | <UITabBarButton: 0x8c64980; frame = (2 1; 236 48); opaque = NO; layer = <CALayer: 0x8c684c0>>
| | | | <UITabBarButtonLabel: 0x8c64dd0; frame = (108 35; 21 12); text = 'Item'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8c64ef0>>
| | | <UITabBarButton: 0x8c69d90; frame = (242 1; 236 48); opaque = NO; layer = <CALayer: 0x8c6a250>>
| | | | <UITabBarButtonLabel: 0x8c69e70; frame = (108 35; 21 12); text = 'Item'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8c68c20>>
| | | <UIImageView: 0x8a49240; frame = (0 -0.5; 480 0.5); autoresize = W; userInteractionEnabled = NO; layer = <CALayer: 0x8a492d0>>
where the important part is:
<UILayoutContainerView: 0x8c6a320; frame = (0 0; 320 480); autoresize = W+H;
| | <UITransitionView: 0x8c6a880; frame = (0 0; 480 320);autoresize = W+H
| | | <UIViewControllerWrapperView: 0x8c6cd70; frame = (0 0; 320 480); autoresize = RM+BM;
| | | | <UIView: 0x8c51070; frame = (0 0; 480 271); autoresize = RM+BM
Note that the UIViewControllerWrapperView has a bad size because its autoresizing mask is not W+H but RM+BM. The root cause however is the autoresizing mask of the UIView because UIViewControllerWrapperView is generated dynamically and the mask is only copied.
If you inspect the source code of the storyboard, you will see this line:
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
For the view in the first view controller.
Replacing it with
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
will fix the problem.
#Desdenova: rdurand is absolutely right. I did put some log statements in the view did load and view will appear and here is what it looks like.
2014-02-14 09:04:19.167 TapTest[3395:60b] The frame is {{0, 0}, {320, 568}}
2014-02-14 09:04:19.174 TapTest[3395:60b] The frame in viewwillappear is {{0, 0}, {320, 519}}
2014-02-14 09:04:27.814 TapTest[3395:60b] The frame in viewwillappear is {{0, 0}, {568, 271}}
The very first time in viewDidload and viewwillappear, the frame is 320,568, but when you go to the second view and come back, the frame is set properly 568,271. So the solution would be setting the frame right while the view controllers are added to the tabbarcontroller
I think this comes from your app changing orientation. If you launch the app and tap on the left side (about the first third of the image) of the second UIImageView, it logs "Tap 2" correctly. I think what happens is that your app launches in portrait, switches to landscape, and messes your gesture recognizer's frame of action. When you leave and come back to the tab, the app is already in landscape, so the frame is updated correctly.
Solution : not a single clue. I only work on portrait apps, so I don't really know what happens with this orientation. That being said, the origin of the problem might not be exactly what I said. But I'd say it's something to look into.
Edit :
I logged the tap gesture x position in the main view :
NSLog(#"%f", [((UITapGestureRecognizer*)sender) locationInView:self.view].x);
The maximum x position where the gesture is recognized seems to be… 320, which is the width of a portrait app. This seems to confirm my idea of the orientation doing something to your views..
Edit 2 : Solution !
I found this thread : Landscape tab bar.
The issue comes with a UITabBarController in landscape mode. Just add the following in your viewDidLoad :
self.view.autoresizesSubviews = YES;
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
I tested it and it works fine now. Remember to upvote Josh Adams' answer as it solves your problem ;]

UIActionSheet is clipped by its superview, I don't have a toolbar or tabbar

My app uses a UINavigationController, but I do not show the toolbar because all of my navigation is controlled by in-game controls.
I do not have a Tab bar, because I'm not using a TabBarController.
My game app is Landscape only.
This is how I previously created and presented the UIActionSheet:
UIActionSheet *quitGameSheet = [[UIActionSheet alloc]
initWithTitle:#"Quit your game?"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:#"Yes, quit game"
otherButtonTitles:nil];
[quitGameSheet showInView:self.view];
However, the UIActionSheet is mostly off screen and I get this error:
Presenting action sheet clipped by its superview. Some controls might not respond to touches. On iPhone try -[UIActionSheet showFromTabBar:] or -[UIActionSheet showFromToolbar:] instead of -[UIActionSheet showInView:].
However, I do not have a toolbar and I do not have a tabbar.
I've tried several other Stack Overflow answers, none of which work for me:
Issue with UIActionSheet
I tried presenting it from self.navigationController.view, from self.parentViewController.view, from a CGRect that I created which was at the bottom of the view, from self.view.bounds because I was desperate.. none of it works for me.
It works in iOS 6, but does not work in iOS 7. Here are a couple of screenshots of it working on iOS 6 and failing on iOS 7.
Any help?
UPDATE 1 ---
Here is a view hierarchy as requested:
<UIView: 0xc480380; frame = (0 0; 568 320); autoresize = RM+BM; layer = <CALayer: 0xc4803e0>>
| <UIImageView: 0xc480410; frame = (0 0; 568 320); opaque = NO; autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0xc4804a0>>
| <UIButton: 0xc47b370; frame = (203 107; 159 37); opaque = NO; autoresize = LM+RM+BM; layer = <CALayer: 0xc47a730>>
| | <UIImageView: 0xc475150; frame = (0 0; 159 37); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xc4751e0>>
| <UIButton: 0xc478a00; frame = (203 152; 159 37); opaque = NO; autoresize = LM+RM+BM; layer = <CALayer: 0xc477dd0>>
| | <UIImageView: 0xc475030; frame = (0 0; 159 37); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xc4750c0>>
| <UISwitch: 0xc47dcc0; frame = (313 65; 51 31); opaque = NO; autoresize = LM+RM+BM; layer = <CALayer: 0xc47d180>>
| | <_UISwitchInternalViewNeueStyle1: 0xc47e040; frame = (0 0; 51 31); gestureRecognizers = <NSArray: 0xc47ffa0>; layer = <CALayer: 0xc47e140>>
| | | <UIView: 0xc47e4e0; frame = (35.5 0; 15.5 31); clipsToBounds = YES; layer = <CALayer: 0xc47e540>>
| | | | <UIView: 0xc47e330; frame = (-35.5 0; 51 31); layer = <CALayer: 0xc47e390>>
| | | <UIView: 0xc47e450; frame = (0 0; 35.5 31); clipsToBounds = YES; layer = <CALayer: 0xc47e4b0>>
| | | | <UIView: 0xc47e3c0; frame = (0 0; 51 31); layer = <CALayer: 0xc47e420>>
| | | <UIView: 0xc47f750; frame = (0 0; 51 31); layer = <CALayer: 0xc47f7b0>>
| | | | <UIImageView: 0xc47f480; frame = (39 16; 0 0); alpha = 0; userInteractionEnabled = NO; layer = <CALayer: 0xc47f660>>
| | | | <UIImageView: 0xc47f690; frame = (12 16; 0 0); userInteractionEnabled = NO; layer = <CALayer: 0xc47f720>>
| | | <UIImageView: 0xc47e680; frame = (7 -6; 57 43.5); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xc47f2f0>>
| <UILabel: 0xc480610; frame = (173 63; 96 29); text = 'Sounds:'; clipsToBounds = YES; opaque = NO; autoresize = LM+RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0xc480540>>
| <UIButton: 0xc473a20; frame = (203 197; 159 37); opaque = NO; autoresize = LM+RM+BM; layer = <CALayer: 0xc473590>>
| | <UIImageView: 0xc474f30; frame = (0 0; 159 37); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xc474fc0>>
I had what appeared to be a completely unrelated problem in the same app, wherein some of the buttons on the left side of the screen weren't tappable.
Can't tap buttons on the left side of 4" iPhone
While investigating that I discovered that my UIWindow was 320x480. The problem was in my MainWindow.xib. Xcode created this xib for me, but it was probably Xcode 3 and it was several years ago. I decided to review the settings and I noticed the option for "Full Screen at Launch." I looked up that option in the docs and there was a note stating that this should always be on so that the Window is properly resized to match the device size. This was never an issue when there was only one size iPhone, but it became a problem with the introduction of the 4" iPhone.
So, I checked that box, and relaunched and viola, both this and the other problem were solved because the enclosing UIWindow was correctly sized.
I have recently solved a similar issue happening on iOS 7 (although it did not involve an action sheet) by presenting in the main window; in your case it would be something like:
[quitGameSheet showInView:[UIApplication sharedApplication].keyWindow];
hope it will work in your case as well.
[sheet showInView:[UIApplication sharedApplication].keyWindow];
sheet.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height-
sheet.frame.size.height, [UIScreen mainScreen].bounds.size.width,
sheet.frame.size.height);
Link: Issue with UIActionSheet
Form: user352537

Issue with constraints on bottom layout

I'm getting some issues with a constraint relative from the bottom layout.
There are some views inside a UITabBarViewController. When I change to another view from the tab and return to first one, the viewcontroller not recognize the bottom layout as the tab bar, but from the bottom of the view.
I uploaded the problem here: https://sites.google.com/site/rveducationapps/layoutError.png?attredirects=0&d=1
I already tried put in ViewDidAppear and also ViewWillAppear:
[self.view layoutSubviews];
[self.view setNeedsUpdateConstraints];
I logged the subviews in the two cases, the right one and after I exit and return to the view:
subviews:(
"<UIView: 0x146b2260; frame = (210 0; 814 712); autoresize = RM+BM; layer = <CALayer: 0x146b22c0>>",
"<UIButton: 0x146a9eb0; frame = (727 672; 287 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x146aa070>>",
"<UITableView: 0x151d3400; frame = (0 0; 210 761); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x146b14b0>; layer = <CALayer: 0x146ade20>; contentOffset: {0, 0}>",
"<_UILayoutGuide: 0x146b23b0; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x146b24a0>>",
"<_UILayoutGuide: 0x146b28a0; frame = (0 712; 0 56); hidden = YES; layer = <CALayer: 0x146b2910>>"
)
subviews:(
"<UIView: 0x146b2260; frame = (210 0; 814 768); autoresize = RM+BM; layer = <CALayer: 0x146b22c0>>",
"<UIButton: 0x146a9eb0; frame = (727 728; 287 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x146aa070>>",
"<UITableView: 0x151d3400; frame = (0 0; 210 817); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x146b14b0>; layer = <CALayer: 0x146ade20>; contentOffset: {0, 0}>",
"<_UILayoutGuide: 0x146b23b0; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x146b24a0>>",
"<_UILayoutGuide: 0x146b28a0; frame = (0 768; 0 0); hidden = YES; layer = <CALayer: 0x146b2910>>"
)
I see there are some differences, specially in:"<_UILayoutGuide: 0x146b28a0; frame = (0 712; 0 56); hidden = YES; layer = <CALayer: 0x146b2910>>"
But I don't know how to fix it.
EDITED:
I think I fixed. I selected the UIViewController in the Storyboard and disabled the EXTEND EDGES->Under Bottom Bars option.
If you are transitioning from your UITabBarViewController to a new UIViewController, and then attempting to go back to your tabBarController from that UIViewController, then you could simply segue back to the UITabBarViewController(The segue will take you back to your rootViewController for your UITabBarViewController).
If you need to transition back to a specific tab, instantiate a custom segue and send it back to a different tab using a different specified VC, this can be set in your UITabBarViewController class or in your applications AppDelegate.m file.

Resources