UIButton in scrollView unresponsive when outside initial frame - ios

I have a very long view (>2000 pixels) created in a xib. This xib is loaded and then, inside it's ViewController I switch the root view for a scroll view (I want that 2000 pixels to scroll);
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *view = self.view;
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:self.view.frame];
scroll.contentSize = self.view.bounds.size;
self.view = scroll;
[scroll addSubview:view];
}
The long view has a button but it does not fire if it's placed outside the top of the long view (i.e. outside the initial screen frame).
This post seems similar but is slightly different as that SO user is extending the size of the scroll view dynamically. In this case, the scroll view of fixed size (full screen) but I can't hit things in my xib that are outside of the initial frame. During debug, I can see the frame of both the scroll view and content view are set (and long)...
<UIView: 0x946db50; frame = (0 0; 320 2320); autoresize = W+H; layer = <CALayer: 0x946db20>>
<UIScrollView: 0x9077e10; frame = (0 0; 320 2320); clipsToBounds = YES; gestureRecognizers = <NSArray: 0xb388bd0>; layer = <CALayer: 0x9276f60>; contentOffset: {0, 0}>
How can I ensure that taps are making it to controls scrolled into view?

Through trial and error I fixed this, but I'm not sure why it works. In the code above, I init the UIScrollView to have the same size frame as the content view (which actually doesn't make sense, not sure why it scrolls at all).
Instead, I got it working to sizing to the screen (since it will be full screen):
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:[UIScreen mainScreen].bounds];
And now it all works swimmingly. Odd; would love to understand why this is.

UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
this will create a scrollView to the match the actual view of view Controller then
UIView *newView=[[UIView alloc] initWithFrame:CGRectMake(0,0,320,2320)];
[scrollView addSubview:newView];
//u can also create this view from your long view in story board, never use the self.view inside scroll this make Ur hierarchy critical add on more long view inside the controller then refer that view to new instance in view-controller then use here
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width, newview.frame.size.height);
then set
OLD answer
Set contentsize property of ur scroll view to macth the height of ur scroll view and width of your view inside scroll view...

Related

Objective-C - When I add a subview, my subview is going out of the screen at the bottom. Misunderstanding of frames?

Im practicing objective-C, and I try to do everything programmatically.
I'm making a simple view that I add on my view of the ViewController, but this subview is going out of the screen.
When I set my frame, the position for the X and Y are respected, but the rest, no...
Here is the screenshot of my result :
As you can see... The red subview is going out of the screen.
Here is my loadView where I add that subview :
HomeViewController.m - loadView
-(void)loadView
{
self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(15, 15, self.view.frame.size.width - 30, self.view.frame.size.height - 30)];
[subview setBackgroundColor:[UIColor redColor]];
[self.view addSubview:subview];
}
For the padding, I did put 15 for the position x and y... And for the frame, I did calculate with the size of the self.view by removing paddings... As you see, it works well for the width, but for the height, it is a big fail. It goes outside the screen.
In my AppDelegate.h, I set the navigationController.navigationBar.translucent = NO;, in order to that when I set position for x, and y, it starts well after the navigationBar .
I don't understand this weird behavior for the height... If someone has a good explanation for this please.
Thanks
First, you shouldn't rely on the value of self.view in viewDidLoad. It is set to a correct value later, in viewWillAppear:. You can keep your code, if you make your subview resize automatically when self.view is displayed. For that, set autoresizingMask on the subview:
subview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
(or add an equivalent set of constraints if you use Auto Layout.)
Also, I recommend to use bounds instead of frame:
UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(15, 15, self.view.bounds.size.width - 30, self.view.bounds.size.height - 30)];
It doesn't make a difference here, but it often does, e.g. if you calculate the frame's x and y based on the parent frame.
loadView method just creates the view. At the point when loadView gets called there is no information about final view frame hence its children views cannot be placed properly.
The right place to update your children views' frames is viewDidLayoutSubviews:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// update child view frame here
}
Remarks: you can define auto-layout constraints of your child view in code and they will be automatically applied to child views when view controller's view gets resized.

UIScrollview is not scrolling

I know there are tens of similar question about scrollview but I have tried everything, Scrollview is not scrolling.
First I created a uiviewcontroller with a xib file, and set it to freeform and arrange the size of it and added a scrollview on view.
Then added it as a childviewcontroller to another view controller.
-(void)scrollViewSetup
{
if (!_scrollView) {
_scrollView=[[UIView alloc] initWithFrame:CGRectMake(0,200,self.scrollView.frame.size.width,200)];
[self.view addSubview:_scrollView];
ScrollView *displayPortf=[[ScrollView alloc] init];
//add as a child view controller here
//displayPortf.delegate=(id)self;
[_scrollView addSubview:displayPortf.view];
[displayPortf didMoveToParentViewController:self];
[self addChildViewController:displayPortf];
}
}
Content size of scroll view is (768,200) on iPad.
I tried setting content size to (1920,200) in viewDidLoad didn't work then viewDidAppear didn't work then viewDidLayoutSubviews didn't work.
Then I checked the use auto layout box and tried to add constants by goingeditor->resolve auto layout issues->add constraints
How can I make scrollview to scroll?
Note: Parent view uses auto layout and storyboard.
I guess that the reason of issue is zero width of scrollView.
When you call this:
_scrollView=[[UIView alloc] initWithFrame:CGRectMake(0,200,self.scrollView.frame.size.width,200)];
_scrollView is initiated with width self.scrollView.frame.size.width, but at this moment self.scrollView is nil as it related to instance variable _scrollView that hasn't initiated yet.
Try to set some non zero float value for view width:
_scrollView=[[UIView alloc] initWithFrame:CGRectMake(0,200,768,200)];

Why is my UIView's frame changing unexpectedly after the embedded UITableView is interacted with?

Consider the following UIView "MainView":
The view includes a Container View which in turn houses a UITableView controller. The container view's y coordinate starts just beneath the gradient bar. The UITableView includes the section footer at very bottom with the 'STU' label and 'chart' button.
When the UIView loads, and up-to-and-until any interaction with the tableView, MainView's dimensions are:
Frame: 0.000000x, 0.000000y, 568.000000w, 268.000000h
I have a delegate protocol set up such that tapping the chart button in the tableView will create a new view in MainView for a shadow effect via a method performing:
CGRect newFrame = self.view.frame; // self = MainView
newFrame.size.width = 100;
newFrame.size.height = 50;
UIView *backgroundShadowView = [[UIView alloc] initWithFrame:newFrame];
backgroundShadowView.backgroundColor = [UIColor blackColor];
// Do Animation
The important part above is the 'newFrame' CGRect. For some reason after interacting with the table view by tapping the chart button, or even scrolling or tapping a row, self.view.frame suddenly has the following dimensions:
Frame: 0.000000x, 52.000000y, 568.000000w, 268.000000h
And so the shadow view appears as follows, with a y origin much farther down than where it would be expected to start, just above the gradient bar.
I've adjusted the width and height of the "shadowview" for this question; normally it would be 568x268, but would extend 52 units off screen on the bottom because of this issue.
52 units is exactly the height of the statusbar (20) + navigationbar_in_landscape (32).
Of course I could manually adjust the frame dimensions, but I do not want to. I want to know why the view's frame is changing unexpectedly.
For the life of me, I cannot figure out why the view becomes suddenly offset. Any help is appreciated!!
Two comments.
(1)
This code was probably always wrong:
CGRect newFrame = self.view.frame; // self = MainView
newFrame.size.width = 100;
newFrame.size.height = 50;
UIView *backgroundShadowView = [[UIView alloc] initWithFrame:newFrame];
You surely want to define backgroundShadowView's frame in terms of self.view's bounds, not its frame as you are doing in the first line here.
(2)
The change in self.view.frame is probably illusory. You are probably checking this initially in viewDidLoad. But that is too soon; the view has not yet been added to the interface, and so it has not yet been resized to fit the surroundings.

Unknown imageview in uiscrollview

I simply added new uiscrollview from interface builder and in viewDidLoad method I am getting its subview and printing it on console. It shows two image views. I can't figure it out where did they come from.
NSArray * views = [friendScrollView subviews];
NSLog(#"views are %#", [views description]);
In console:
views are (
"<UIImageView: 0x1b82f0; frame = (805 203; 7 7); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x1b6cb0>>",
"<UIImageView: 0x17b1d0; frame = (805 203; 7 7); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x1bdbd0>>"
)
When I remove them they don't even remove from scroll view.
any idea?
These image views are the horizontal and vertical scroll indicator images. They are present by default when you create the UIScrollView instance.
The iOS HIG guidelines for UIScrollView specify
When a scroll view first appears—or when users interact with it—vertical or horizontal scroll indicators flash briefly to show users that there is more content they can reveal.
This is a best practice recommended to be followed to ensure a good user experience. Hence they should not be removed. If at all it is required that the scroll indicators are not to be shown then the scroll view provides properties to achieve this programmatically. Set the required property to NO
showsHorizontalScrollIndicator
showsVerticalScrollIndicator
They are the vertical and horizontal scroll indicators and they are not supposed to be removed
UIKIT_CLASS_AVAILABLE(2_0) #interface UIScrollView : UIView <NSCoding> {
...
UIImageView* _verticalScrollIndicator;
UIImageView* _horizontalScrollIndicator;
Have you tried the following:
for (UIView *v in friendScrollView.subviews)
{
[v removeFromSuperview];
}
Also use isKindOfClass:[UIImageView class] to only remove imageviews from scrollview in case you have subview of friendScrollView other than imageviews.

How to understand the behavior of table.autoresizingMask?

I have learned that the proper way to set a UITableView that covers up the whole main view (full width and full height), in a Single View app is, in viewDidLoad:
table = [[UITableView alloc] initWithFrame:self.view.bounds];
table.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
[self.view addSubview:table];
note that if a physical iPad 2 is held at Landscape mode, and if the self.view.bounds above is printed inside viewDidLoad, it will show: {{0, 0}, {768, 1004}}. So I thought the idea is: don't worry the width and height not being correct, because the UIViewAutoresizingFlexibleWidth and UIViewAutoresizingFlexibleHeight will take care of setting the correct width and size automatically.
So I actually tried replacing the above first line by:
table = [[UITableView alloc] init];
or
table = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
or even
table = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 1004, 768)];
or the ultimately "correct value":
table = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 1024, 748)];
when the iPad 2 is held at Landscape position. But now the table won't fully expand to the whole main view. So what is the governing principle here? The width and height can be set incorrectly, but it must be incorrectly at {768, 1004}? It can't be "incorrect" with other values? If no CGRect was given, or some dummy values {200, 200} was given, what should the code in viewDidLoad do to make the table have the full main view's width and height whether it is Landscape or Portrait mode?
dont use bounds, use frame instead:
table = [[UITableView alloc] initWithFrame:self.view.frame];
table.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
UIAutoresizingFlexibleWidth means that the view's width will expand and shrink when its superview's width expands and shrinks. UIAutoresizingFlexibleHeight means the same thing for height. Adding these is equivalent to turning on "springs" in Interface Builder. I think that if there are no "struts" set, when the superview doubles in width, the view will double in width as well.
The strut settings are:
UIViewAutoresizingFlexibleRightMargin
UIViewAutoresizingFlexibleLeftMargin
UIViewAutoresizingFlexibleTopMargin
UIViewAutoresizingFlexibleBottomMargin
If UIViewAutoresizingFlexibleRightMargin is NOT set, the right strut is turned on. This means that the space between the right side of your view and the right side of its superview will not change when the view expands. The same concept holds for all the other margins.
There is as good discussion here: https://stackoverflow.com/a/10470469/472344.
I think you are confused by the self.view.bounds that you are getting. This is the bounds of the view before rotation, as jrturton pointed out the height is 1004 because 20 pixels are subtracted for the status bar. I don't know why after rotation the width and height haven't updated (I'm assuming your rotation happens before loading the view, otherwise the printed out bounds are exactly what you should expect), but I have noticed this as well after rotation. I have seen some discussion on Stackoverflow about this, but I can't find it at the moment.
My guess is that as jrturton said, when the view is loaded, it uses the bounds set in the xib (or the storyboard), and the resizing happens after viewDidLoad. Maybe try printing out the bounds of self.frame.view in viewDidAppear, as the bounds should have changed at that point.
To address the behaviour of the auto resizing mask settings again, understand that these do NOTHING unless the view's superview changes size. The auto resizing mask settings will tell your view how to automatically resize when its superview changes size. If you want your table view to have the same size as self.view always (such as after rotation, or zooming), you need to set it exactly as you have done:
table = [[UITableView alloc] initWithFrame:self.view.bounds];
table.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
[self.view addSubview:table];
1004 is correct - the status bar is 20px. Your first code snippet is fine.
Note that in viewDidLoad your view's orientation will still be in portrait - hence the numbers you are seeing. The resize happens afterwards. Using the bounds rectangle of the main view as the frame of a subview will always let the subview fill the whole view.

Resources