iOS - Background view is consuming touch events - ios

I'll start by describing how the screen looks. It is a UIPageViewController that covers 100% of the screen and a UIView (on top of the page vc) that covers 20% of the screen. Easy, right? basically UIView is over UIPageViewController.
So, I'm adding said UIPageViewController programatically to my view controller with self.view.addSubview(pageVC.view). but the UIView is defined in the interface builder
For this reason, since the UIPageViewController is added last it covers the UIView which forced me to call myUIView.layer.zPosition = 1 so that the UIView is visible.
The problem is any touch made on the UIView is instead consumed by the UIPageViewController. how can I solve this?

The layer property of a particular view is only used for the rendering of that specific view. That's the reason why changes to the layer won't impact anything regarding the interaction with that specific view.
However, the -sendSubviewToBack: and -bringSubviewToFront: methods on UIView were designed to achieve exactly what you are looking for. Here you can read the docs for them.
I wrote this piece of code so that you can test what this method is doing. Try to comment/uncomment the last line or changing the order in which the subviews are added to get a better understanding of how it works.
UIView *firstView = [[UIView alloc] initWithFrame:CGRectMake(200, 200, 100, 100)];
UIView *secondView = [[UIView alloc] initWithFrame:CGRectMake(200, 200, 150, 150)];
firstView.backgroundColor = [UIColor greenColor];
secondView.backgroundColor = [UIColor redColor];
[self.view addSubview:firstView];
[self.view addSubview:secondView];
UITapGestureRecognizer *firstTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(firstTap)];
UITapGestureRecognizer *secondTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(secondTap)];
[firstView addGestureRecognizer:firstTap];
[secondView addGestureRecognizer:secondTap];
[self.view sendSubviewToBack:secondView];

Related

Nested uiscrollviews and custom handling event routing

I have nested UIScrollViews in a such way that inner UIScrollView is located at second page of outer UIScrollView. (i.e. inner one's frame would be CGRectMake(320, 0, view.width, view.height)). Two scrollviews only goes horizontally. I would like to swipe only inner scroll view once it is on the screen (i.e outer scroll view moves to second page) and until inner one reaches to the end. Once inner one reaches to the end, then I would like to recognize swipe for outer one. I first try to set outerScrollView.scrollEnabled = NO on second page, but then inner one also didn't get received swipe gesture. I also tried to subclass UIScrollView and override hitTest:event to return inner one's UIView, that also didn't work out. Is there a way to handle swipe event to a specific view and block for other views?
Try detecting when the scroll view reaches the bottom of the inner view by using the code here. If it's reached the bottom, flip a boolean (remember to flip it back if the user moves the scroll view away from the bottom!). If the boolean is flipped, use the code here to pass the touches to the superview and allow horizontal scrolling of the containing UIScrollView. If you're having trouble forcing the scroll try the code here to force scrolling.
EDIT:
I just wrote some code to test this out. By restricting the directions each view could scroll, I was able to get it to work as you described without using most of what I referred to above (much simpler). Here's the code for the scroll views:
outer = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
[outer setBackgroundColor:[UIColor redColor]];
[outer setShowsHorizontalScrollIndicator:true];
[outer setShowsVerticalScrollIndicator:false];
[outer setScrollEnabled:true];
[outer setDelegate:self];
[outer setAlwaysBounceHorizontal:true];
[outer setAlwaysBounceVertical:false];
outerContent = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width*2, self.view.bounds.size.height)];
[outerContent setBackgroundColor:[UIColor blueColor]];
[outer addSubview:outerContent];
outer.contentSize = outerContent.frame.size;
[self.view addSubview:outer];
inner = [[UIScrollView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
[inner setBackgroundColor:[UIColor yellowColor]];
[inner setShowsHorizontalScrollIndicator:false];
[inner setShowsVerticalScrollIndicator:true];
[inner setAlwaysBounceHorizontal:false];
[inner setAlwaysBounceVertical:true];
[inner setScrollEnabled:true];
[inner setDelegate:self];
innerContent = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height*2)];
[innerContent setBackgroundColor:[UIColor greenColor]];
inner.contentSize = innerContent.frame.size;
[inner addSubview:innerContent];
[self.outerContent addSubview:inner];
I color coded the views so you could tell what was going on. "inner" and "outer" are UIScrollViews that I declared in my header file and synthesized in the class. "innerContent" and "outerContent" are UIViews also in the header file.

iOS - Why setting and displaying subviews in initWithNibName doesn't work?

I have a simple xib file which its layout is:
Which the pale green UIView is referenced to an IBOutlet view_content in my UIViewController class. Then I did the following in initWithNibName:bundle: of my ViewController.m:
self = [super initWithNibName:nibNameOrNil bundle:nil];
if (self) {
self.view_a = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
self.view_a.backgroundColor = [UIColor purpleColor];
[self.view_content addSubview:self.view_a];
self.view_b = [[UIView alloc] initWithFrame:CGRectMake(150, 150, 100, 100)];
self.view_b.backgroundColor = [UIColor blueColor];
[self.view_content addSubview:self.view_b];
self.view_c = [[UIView alloc] initWithFrame:CGRectMake(200, 200, 100, 100)];
self.view_c.backgroundColor = [UIColor greenColor];
[self.view_content addSubview:self.view_c];
}
return self;
I expected there will be 3 small squares, each with different colors as declared in the code above, but they did not show up which the view is loaded and showed. But instead if I move the addSubView: calls into viewDidLoad, they are displayed as expected. I tried to test if view_content is nil in the if-segment and seems it is nil at that time. I don't understands why this is happening and how it works, isn't the view and its components are supposed to be loaded from the xib?
Thanks for any help!
At the point initWithNibName:... is called, the view itself is not yet loaded. This is an optimization (lazy loading) that defers loading the view until it is actually needed. The view is actually loaded when viewController.view is first-time accessed, and that's when viewDidLoad is called.
Some people force the view controler to load its view by calling [viewController view] (or [self view] if it's within the view controller's class).
The reason is simple. View is not loaded at that time when you are initializing the controller means initWithNibName. So best place is viewDidLoad where your all outlet has been assigned.

Superview isn't getting touch events

I have a simple iPad app with the following layers of views:
BaseView (covers the entire available area).
SquareView (covers 100x100px area).
The SquareView is added as subview to BaseView.
I also have a BaseViewController.m file. In this file I added the touchesBegan and touchesEnded functions to handle touch events. The problem is, when I click on the SquareView the touches functions get called, but when I click anywhere else they don't get called.
I have userInteractionEnabled = YES for the SquareView. I need this variable to be set in order to receive touches.
Clicking on the area outside the SquareView does not trigger the touches functions. I am puzzled as to why. Setting the userInteractionEnabled variable in the BaseView has no impact. What could be happening here? Is there some rule that I am missing? I am pretty sure I had it working at one point.
When the user clicks on the subview (SquareView) I need to launch a menu (let's say MenuView class). The idea is, when the user clicks on the area outside of the menu (or SquareView) I need to remove the pop-up MenuView by calling [menuView removeFromSuperview]. Is there a better way to handle this?
You could try to use this code in your controller. It should work for you.
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
subview.backgroundColor = [UIColor lightGrayColor];
[subview addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(subviewTouched)]];
[self.view addSubview:subview];
[self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(viewTouched)]];
}
- (void)viewTouched
{
NSLog(#"viewTouched");
}
- (void)subviewTouched
{
NSLog(#"subviewTouched");
}

Changing programatic uiscrollview to IB

got a bit of a small problem here. I've been following this tutorial which creates an automatic scrolling slideshow using uiscrollview. This piece of code is used in my viewdidload -
UIScrollView *scr=[[UIScrollView alloc] initWithFrame:CGRectMake(0, 120, 320, 180)];
scr.tag = 1;
scr.autoresizingMask=UIViewAutoresizingNone;
[self.view addSubview:scr];
[self setupScrollView:scr];
UIPageControl *pgCtr = [[UIPageControl alloc] initWithFrame:CGRectMake(0, 280, 320, 60)];
[pgCtr setTag:12];
pgCtr.numberOfPages=10;
pgCtr.autoresizingMask=UIViewAutoresizingNone;
[self.view addSubview:pgCtr];
As you can see a small uiscrollview is created programatically which is 320x180. It works perfectly except for the fact that my UI has another uiscrollview which takes up the whole ui, which is around 320x700. I need to embed this programatically created scrollview into it, any ideas? Or alternatively I need to create my own scrollview using storyboards and link it across using code, but have no idea how I would go about this.
Any ideas would be much appreciated.
You have to embed the scroll view inside the scroll view not the main view
[self.scrollView addSubview:scr];

Replacing self.view with new UIView shows black view

I want to change the existing view in a UIViewController to a new view. The new view contains the old view and a little banner view.
Doing this fairly simple change leaves me with a black view.
My code looks like this
UIView *existingView = self.view;
UIView *newView = [[UIView alloc] initWithFrame:existingView.frame];
UIView *bannerView = [[UIView alloc] initWithFrame:CGRectMake(0, (self.view.frame.size.height - 50), 320, 50)];
CGRect existingViewFrame = existingView.frame;
existingViewFrame.size.height -= 50;
existingView.frame = existingViewFrame;
[newView addSubview:existingView];
[newView addSubview:bannerView];
self.view = newView;
However when switch Tabs and come back to the view which changed the view is shown just like I want. I guess I need to set a flag or something to tell the controller to redraw it's (new) view.
Edit
I wrote an simple example for this problem. You can find it on GitHub: https://github.com/Oemera/ChangeView
You did not say where you do this. It may be that you need to save the original view's super view, then add the new view to that views subViews array. I'm betting that is the problem.

Resources