Weird behaviour of custom UIViews when touched - ios

In a what would be a (yet another) word game for iOS I have a scroll view holding an image view with game board.
And underneath I have draggable custom views representing letter tiles.
For the scroll view I've added a double tap gesture recognizer for toggling its zoomScale (between 50% and 100% image width):
This works well, but I had to disable Autolayout (because otherwise scroll view's contentOffset would jump to {0,0} when I dragged the tiles).
So I had to add the following code to ViewController.m to adjust scroll view's frame after device rotation:
- (void) adjustSubViews
{
_scrollView.frame = CGRectMake(0,
0,
self.view.bounds.size.width,
self.view.bounds.size.height - kHeight - 2 * kPadding);
}
this seems to work too - but my problem is that after first device rotation to landscape and back "something breaks" and the tiles are suddenly draggable by touching underneath them:
Why does it happen please?
Below is an Xcode screenshot (here fullscreen) showing the scroll view properties:
UPDATE: I've added size logging to the Tile.m:
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
NSLog(#"%s %#", __PRETTY_FUNCTION__, NSStringFromCGSize(self.frame.size));
[_smallImage setHidden:YES];
[_smallLetter setHidden:YES];
[_smallValue setHidden:YES];
[_bigImage setHidden:NO];
[_bigLetter setHidden:NO];
[_bigValue setHidden:NO];
[self.superview bringSubviewToFront:self];
}
and while initially it logs the expected tile size of 45x45 (set by me in Tile.xib):
-[Tile touchesBegan:withEvent:] {45, 45}
After I rotate the device to landscape and back to portrait I suddenly get the height of 160:
-[Tile touchesBegan:withEvent:] {45, 160}

The tile becomes draggable blow its image because the view containing the tile is auto-resized by it's superview (controller's view) that changes its frame during rotation.
This behaviour can be easily avoided by setting autoresizing mask of the tile view to UIViewAutoresizingNone (by deselecting all springs and struts in Interface Builder).
Note the tools to debug problems with view sizing like this one:
LLDB command po viewVariableOrAddress (calls description method of its parameter and prints the result).
-[UIView recursiveDescription] called from LLDB by the following command: po [viewVariableOrAddress recursiveDescription]. Note that his method is private and must not be called in production code (the app will be rejected otherwise).
Reveal — in a natural GUI, represents the same information that you can get at runtime in debugger console.

Related

UIButton created outside view not working

I have an application I'm making in storyboards. I want to have a tutorial-type of view. I decided to go with a freeform view controller and am filling it with 600x600 views that count as the pages. The problem I'm having is that when I have a UI button animate to the next page, the buttons that were created outside of the visible view don't seem to work. I even moved a view over so the button was half-visible and only half the button works when I move the view over.
Here's my next page code:
- (void)nextPage {
if (scrolling) return;
scrolling = YES;
[UIView animateWithDuration:0.3 animations:^{
CGRect frame = self.tutorialView.frame;
frame.origin.x -= 50; //frame.size.width;
[self.tutorialView setFrame:frame];
}];
scrolling = NO;
}
I currently have it moving only 50px instead of the whole page for testing purposes.
For testing purposes, I've started it half-way through and only half the button works. I've started it with the second view on top of the other one halfway visible and the same thing happens (only half the button works). Otherwise, when I tap the next button on the first view the buttons on the second view don't work (buttons created outside the initial view).
Views don't receive touch events where they're outside the bounds of their superview. You'll need to increase the button's superview's size.
You can visualize this behavior by setting clipsToBounds = YES - then you'll only see the touchable area of your button.
(You can override this behavior, but you probably shouldn't.)

iOS 7 UIScrollView doesn't scroll when presented as modal view controller, works fine otherwise

I have a storyboard in which I have a view controller, (InfoViewController) in which I have an UIScrollView with some labels, uitextviews, etc. this is all created in IB, no code has been written at all. The only thing that is left for me to do is to set the content size, which I do as following:
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
CGRect screenRect = [[UIScreen mainScreen] bounds];
[self.scrollView setContentSize:CGSizeMake(screenRect.size.width, 600)];
[self.scrollView setBackgroundColor:[UIColor greenColor]];
}
Whenever I make this view the entry point of my app, it works perfectly. I can see my view, the content size is set, the background color is being set to green.
Now it comes, I created another view controller, and this view controller is now my entry point of the app. I added a button in there, and on this button I did a "modal segue" to the earlier mentioned Info View Controller.
When I now run my app, I press this button, my Info View Controller shows up. The green background color is being set, but it's impossible to scroll. So the code is being executed (otherwise the background color couldn't been green, in the storyboard it's just plain white) but somehow whenever I use this "modal segue", the scroll functionality gets lost.
How can I fix this?
Try to insert a UIView into the scroll view...
Set the UIView with top, bottom, leading and trailing space to super view to 0.
Then insert everything into the UIView rather than into the ScrollView
Then modify the constraint height of the inner UIView instead of the contentsize of the scroll view, it works with iOS7

Finding The Center of a View

I have an iphone app with 2 ViewControllers . Both screens(viewcontrollers) show a loading screen. I create the loading screen programmatically:
UIView *loadingScreen = [[UIView alloc] initWithFrame:CGRectMake(100,200,144,144)];
loadingScreen.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0);
//{.. Other customizations to loading screen
// ..}
[self.view addSubview:loadingScreen];
For some reason, the second viewcontroller's loadingScreen is significantly lower and it isn't centered on the screen. The first viewcontroller works perfectly and is dead center like I want.
The second viewcontroller is a UITableView and it shows the uinavigationbar, whereas the first viewcontroller doesn't show the uinavigationbar. Also, I use storyboard for my app.
I've outputted to the NSLog self.view.frame.size.height and loadingScreen.center in both instances and THEY HAVE THE SAME COORDINATES! So, not sure why it is showing up lower. Any ideas why the second loadingScreen is lower and how to fix? Thanks!
You mention that one screen displays a UINavigationBar while the other does not. When you display a navigation bar, it offsets the rest of your view - in this case by shifting it down.
There are two quick fixes. You can either adjust your center point up by the size of the UINavigationBar (65 pts - unless it's a custom UINavigationBar and you've changed its size) or you can set the "Adjust Scroll View Insets" value to false in the attributes inspector.
The latter is probably the easiest and comes most recommended. Note though, that the top of your UITableView will now be underneath the UINavigationBar.
My final note would be that if you wanted to do it programmatically than in your UITableView's delegate you can call
- (BOOL)automaticallyAdjustsScrollViewInsets
{
return NO;
}

Weird interface bug UIScrollView in UITabBarController. Reproducible by others?

I get a weird interface bug with my UIScrollView and I cant figure out how to solve it. I only wrote one line of code (shown below) and it is a blank project's setup easily reproducible!
Setting:
I have a UIScrollView that contains a UISegmentedControl (since the segments of
the control are loaded dynamically, it could exceed the width of the screen and the scrollView is supposed to scroll the segmentedControl horizontally, the height of the scrollview is the same as the UISegmentedControl's).
The ViewController that contains this is embedded in a tabBar (or navigation bar, which also shows the bug). The whole thing is using Auto-Layout.
Bug:
When I scroll the SegmentedControl some degree to the right and then switch the viewController by clicking the other tab on the tabBarController, the content-offset of the segmented control gets weirdly shifted when switching back to the initial viewcontroller. When I try to scroll to the leftmost part of the scrollview it won't let me. When switching the tabs a couple of times, it gets fixed again and I can do this over.
What I did (can you reproduce this?):
Create a blank single-view ios project
Embed the already given viewController in a tabbarcontroller.
Put a scrollView on the upper portion of the view that fits the screen from left to right.
Put a UISegmentedControl on the topleft corner of the scrollview and drag the scrollview to fit the segmented controls height height
Change the Segmented control's width a bit so xcode adds a width-constraint. in the segmented control's width constraint change the width constraint's relation to "greater than or equal"
create an outlet to the segmented control
in viewDidload add this code
[self.segmentedControl insertSegmentWithTitle:#"A really long title so it you have to scroll to see it" atIndex: 0 animated: NO];
Create a blank viewcontroller and add it as a second viewController for the tabbarController.
This is how my storyboard looks like:
Now run the project, scroll the segmented control to it's right end as far as it goes. Switch the tab and switch back and please tell me how your scrollview now behaves - and WHY.
My guess would be it has something to do with Auto Layout maybe? Can't figure out what though.
I tried fixing this by setting the scrollView's contentSize in viewDidAppear or changing the content offset of the scrollView in viewDidAppear or changing frames, combination of those and what not....
Extra question:
Is it no longer neccessary to set the scrollViews contentSize property? Why does it scroll the content automatically?
After googeling I found the answer in another StackOverflow question.
What you need to do is save the scrollview.contentOffset on viewWillDisappear,
set it to CGPointZero on viewDidDisappear and set it back to the saved state on viewDidLayoutSubviews:
-(void) viewWillDisappear: (BOOL) animated {
self.lastContentOffset = self.scrollView.contentOffset;
[super viewWillDisappear: animated];
}
-(void) viewDidDisappear:(BOOL)animated {
[super viewDidDisappear: animated];
self.scrollView.contentOffset = CGPointZero;
}
- (void)viewDidLayoutSubviews {
[super viewDidlayoutSubviews];
self.scrollView.contentOffset = self.lastContentOffset;
}

UISearchDisplayController change dimmed view frame

In my iPad app I have an UITableView. Table's frame size is less than screen size, so to make search functionality look nice I have to adjust searchResultTableView's frame to fit my TableView. I'm doing it in my UISearchDisplayDelegate's -searchDisplayController:willShowSearchResultsTableView: method.
Everything works fine except dimming view. When I'm starting search dimming view's width is equal to screen width:
When I start entering search string or clear textfield my searchResultsTableView resizes properly and everything works as it should:
I tried to change searchResultsTableView frame inside -searchDisplayControllerWillBeginSearch: method using this line
controller.searchResultsTableView.frame = myFrame;
but it doesn't work as well. Any suggestions besides implementing my own search display controller?
I also needed to change the frame of the dimming view but for a different reason. In my case I created a UISearchDisplayController and UISearchBar programmatically in a regular UIViewController not a UITableViewController. I was also using MFSideMenu which added to the complexity of the problem. What ended up happening was the dimming view was in the correct position initially but the next time the search was cleared the dimming view shifted leftwards and upwards by exactly half of it's size. Given the UISearchDisplayController you can find the dimming view like so.
for(UIView * v in controller.searchContentsController.view.subviews)
{
if([v isMemberOfClass:[UIControl class]])
{
v.frame = newFrame; //This frame should account for the UISearchBar
}
}
To handle for the initial incorrect frame you should change it in this delegate method:
- (void) searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView;
To handle for an incorrect frame on any subsequent clears you should change it in this delegate method:
- (void) searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView;
Note: this solution runs through the subviews of the searchContentsController which is one of the reasons I used isMemberOfClass instead of isKindOfClass (UIButton is a subclass of UIControl). Further discrimination would be required if you added a UIControl instance into your view (you could use tags to help determine which ones are yours).

Resources