I am working with a custom view which inherits from UIScrollView. The problem I am having is adding a UILabel that should be displayed at the 'top', in front of the other views. It works, but unfortunately it also covers up the scroll indicators.
As you can see, the scroll indicator is being obscured. I don't want to disable scroll indicators.
I am adding the views directly to the UIScrollView:
UILabel *subsection = [[UILabel alloc] initWithFrame:CGRectMake(0, y * h,
[self totalContentWidth], [tableViewDelegate rowHeight])];
[subsection setText:subsectionName];
[subsection setTextAlignment:NSTextAlignmentCenter];
[subSectionRows setValue:subsection forKey:key];
[self addSubview:subsection];
and then bringing them to the front, which
- (void) bringSubsectionsToFront {
for (UIView* row in [self.subSectionRows allValues]) {
[self bringSubviewToFront:row];
}
}
This behavior is confusing to me. If I peek at UIView.layer.zPosition of all the UIScrollView's subviews, they are all the same.
If I adjust the bringSubsectionsToFront method to instead move the labels in front of the view that contains the grid lines, the behavior is the same.
Looking at some internal view classes whose behavior works, it looks as if they are being added to the scroll view with: [self insertSubview:cell atIndex:0];
What am I missing here?
Solution:
verec remarked that I could find the bars if I really wanted to. So I iterated over the subview list at a suitable point, and sure enough, there they were. The order of the objects was as follows:
CustomCell
...
UIImageView
UIImageView
CustomBorderView
UILabel
UILabel
Assuming this is the z ordering, no wonder my labels were always on top.
Solution becomes simple; add UILabel's after CustomCells. This solution only works because the custom table adds CustomCells at index 0, which I believe makes scroll indicators appear after the CustomCells. If this assumption holds, I can reorder the other views relative to the CustomCells, achieving the layering effect I want.
The simplest is to use and intermediate UIView, call it 'contentView' whose children are the views you want to bringToFront.
If that contentView is what is inside the scrollView, it will stay 'below' the scroll indicators whichever contentView child you bring to the front.
The alternative would be to scan into the scrollView looking for the scroll indicators (you will find them!) but that's a compatibility risk, as you never know if/when Apple may decide to remove/reshape/reposition them according to some new (flatter ...) design paradigm ...
Related
EDIT:
Immediately after posting this question, I found ONE line of code I missed commenting out. It was this... mapViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
I commented it out, and the map is working again.
Do you have any idea why that line of code would kill the maps gesture recognizers?
I have an MKMapView that was working perfectly fine in recent months, and quit dead today.
The only thing I did today, messing with the code, was to play with some orientation code in viewWillTransitionToSize:withTransitionCoordinator: and viewWillLayoutSubviews That's IT !!!
I commented out everything I did there, and still it's dead.
It not only won't respond to my own gesture recognizer, it won't respond to any of its own recognizers... pinch, rotate, etc. It's just frozen.
I have NO views on top of the map that would obstruct touches either.
I have control-clean(ed) my build folder.
I have closed XCode project and wiped out it's derived data folder for said project.
#implementation MapViewController
{
MKMapView *appleMapView;
}
-(void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[self addMapViewToSelf];
}
-(void)addMapViewToSelf
{
// Create and add the apple map view to the side menu view.
appleMapView = [[MKMapView alloc] initWithFrame:self.view.frame];
[self.view addSubview:appleMapView];
appleMapView.delegate = self;
UILongPressGestureRecognizer *tapRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(windowTapped:)];
tapRecognizer.minimumPressDuration = 1.0f;
tapRecognizer.delegate = self;
[appleMapView addGestureRecognizer:tapRecognizer];
}
-(void)windowTapped:(UIGestureRecognizer *)recognizer
{
NSLog(#"The recognizer state is '%ld'", (long)recognizer.state);
if (recognizer.state != UIGestureRecognizerStateBegan) {
return;
}
}
AutoResizingMask is the property by by the UI Elements (eg. Label) shift when the boundaries change. Means when orientation is change the app adapts and does not crash. But if you will set autoResizingMask to Constraint, you are telling it to change constraints as per the autoResizingMask. This is the default behaviour of every UI Element. This helps us to quickly and dynamically arrange UI Elements with methods like self.view.centre or self.view.frame or self.label.size.width etc.
Setting translatesAutoresizingMaskIntoConstraints = NO you are actually removing this behaviour. Therefore you must provide the UI Element fixed co-ordinates, so that the UI Element stays there no matter the orientation of screen. But you are not doing so. So the UIElement has no constraints or a fixed location and behave erractically.
You must not set it to NO, if you don't want to play dynamically with UI's location or size. Like input based growing textfield or some crude animation.
Here is Apple trying to tell you the same thing.
When a view’s bounds change, that view automatically resizes its
subviews according to each subview’s autoresizing mask. You specify
the value of this mask by combining the constants described in
UIViewAutoresizing using the C bitwise OR operator. Combining these
constants lets you specify which dimensions of the view should grow or
shrink relative to the superview. The default value of this property
is UIViewAutoresizingNone, which indicates that the view should not be
resized at all.
When more than one option along the same axis is set, the default
behavior is to distribute the size difference proportionally among the
flexible portions. The larger the flexible portion, relative to the
other flexible portions, the more it is likely to grow. For example,
suppose this property includes the UIViewAutoresizingFlexibleWidth and
UIViewAutoresizingFlexibleRightMargin constants but does not include
the UIViewAutoresizingFlexibleLeftMargin constant, thus indicating
that the width of the view’s left margin is fixed but that the view’s
width and right margin may change. Thus, the view appears anchored to
the left side of its superview while both the view width and the gap
to the right of the view increase.
If the autoresizing behaviors do not offer the precise layout that you
need for your views, you can use a custom container view and override
its layoutSubviews method to position your subviews more precisely.
I do such mistakes a million times , this blog has a nice solution to it.
https://www.innoq.com/en/blog/ios-auto-layout-problem/
This one is really bugging me! What is the name of this UIView subclass? I'm not talking about the compass itself, but the two dots at the bottom of the view. I know it's not a private API because I have seen it before. Or am I confused and this is not a UIView at all, but a UIViewController. Which UIView / UIViewController subclass is shown here. It acts like a UIScrollView, but has distinct pages, and has the dots at the bottom of the screen that show the users relative progress through the pages. I have checked this link about UIView subclasses, but became lost after about the 45th one. http://www.themusingsofalostprogrammer.com/2010/09/list-of-every-uiview-subclass.html
(source: tqn.com)
Thankyou for your time.
It is a UIPageControl. It corresponds (or is supposed to correspond) to the number of "pages" the user can scroll to, sideways. Normally, it indicates how many pages there are, and which one we are on, plus it typically provides a way to scroll sideways (by tapping to its left or right).
If I may add to what matt said...
To use a UIPageControl effectively, you also need a UIScrollView that contains the content. An update to the page control should result in a change to the contentOffset of the scrollView as shown in the code below. UIScrollView has a pagingEnabled property that should be set to YES to complete the illusion of paging.
- (IBAction)pageValueChanged:(UIPageControl *)sender
{
// self.pagedView is an IBOutlet to a UIScrollView
[self.pagedView setContentOffset:CGPointMake( sender.currentPage * 320, 0 ) animated:YES];
}
I have a requirement in my app to display a bunch of information that includes both text and images. It will be quite long, so it will need to be scrollable to access all the content.
I know that I can achive this by programmatically adding different UILabels, UIImages etc to a UIScrollView. But this is a proof of concept, so I'm looking for something a little quicker than having to work out all the positioning and code required. The information is static anyway, and does not need to interact with code.
Is there a way to do this using the interface builder (storyboard or xib is fine)?
you definitely can do that if you simply want a quick interface
1.> you might need to know how long is your scroll view, for example in my case, i set it to 1568
2.> Then i drag all the controls that will fit for the first 568 pixel view onto the scroll view and position them.
3.> Then change the Y value for that scroll view to something like - 500, so you can see the rest of the scroll view, and put everything you need there.
4.> After you have all your controls, and remember to set the frame back to 0,0,320,568
5.> last step, in your code, set SCROLLVIEW.contentSize = CGSizeMake(320, 1568);
I would still suggestion don't hard code all those values, but if you are looking for a quick way to do your interface, hope that gives you some ideas.
Just start a new project with a single view, it will come with a xib or storyboard for its single ViewController.
Create a UIView by dragging it into the workspace and place as many Labels, Images and UI Elements as you want.
Open the xib / storyboard and drag a UIScrollView in as your root VC's root view. Drag the view containing your layout into the scrollview, making it the scrollviews only subview.
Done (almost)!
If you launch your app at this point, you'll notice you can't scroll. That is because the scrollview is "too stupid" to adjust the size of its contentSize property on its own.
You'll need some code here, but it is only a tiny snippet and you won't need to touch it again:
Create a new Category on UIScrollView.
In your category's implementation, do:
#implementation UIScrollView (MyHandyCategory)
-(void)awakeFromNib {
NSArray *subViews = [self subviews];
UIView *contentView = [subViews objectAtIndex:0];
[self setContentSize:contentView.frame.size];
}
#end
Done (for real this time)! This will check the size of the view your scrollview contains and ajust the contentSize after it has been initialized. You can change the size of your content view as you like, no need to play around with hardcoded values or even Interface Builder values!
If it’s just proof of concept I’d have a WebView and a local HTML page you load. Easy-peasy.
I would suggest UICollectionView. It's fairly straightforward. There's a good tutorial here:
http://www.raywenderlich.com/22324/beginning-uicollectionview-in-ios-6-part-12
Can someone please guide me to a tutorial , on how to implement a vertical scrolling view on my iOS app?
I can't believe that the last 2 days , I can't find a single working example on just a vertical scrolling view. All the tutorials are about horizontal scrolling, zooming etc etc.
What I am looking for would be something like this tutorial http://www.edumobile.org/iphone/iphone-programming-tutorials/scrollview-example-in-iphone-2/, which shows how to make a uiscrollView and add objects to it, from the Builder and not programmatically. The only flaw I found was , that when trying to add a map down in the scrolling area, the map would appear up and not in the position I placed it. Maybe any ideas on that too?
Anything in mind?
So you want to create a scroll view in xib.
Add a scroll view to the nib file
set its size to to whatever you want
Add your controls (Buttons,labels..)
Add it as a sub view of main view
Finally create a property of scroll view and in viewDidLoad set the content size of the scroll view
self.scrollView.contentSize =CGSizeMake(320, 700);
It simple, same as horizontal scroll view.
Add a scrollview in a view hierarchy,
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:scrollView];
Now to make scroll view scrollable, set its scrollview content size greater than its bound.
[scrollView setContentSize:CGSizeMake(scrollView.bounds.size.width, scrollView.bounds.size.height*3)];
Now it can scroll three time the height of scrollView.
I've struggled with this simple issue for a full day. There are most likely benefits to a more sophisticated approach, but unchecking autolayout solved my issue.
For the quick fix, I believe this is the best approach and will save you a HUGE headache.
Just change the content size of your scrollview.Let's assume you are creating a UIScrollView with the following frame
scl=[[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, 620, 172)];
Now call the content size property of your scroll view,like i have shown below .
[scl setContentSize:CGSizeMake(700, 172)];
Now add the scroll view on a view / View Controller etc and check it .
I've been struggling with this problem for a couple of days now and I can't seem to find any concrete solution online, so here it goes...
The scenario is simple: I want the table view to be expanded (i.e. not scrollable) in a scroll view, and therefore I need to resize and move the view(s) within the scroll view. This I have achieved quite easy by sub classing the UIScrollView and re-implemented the layoutSubviews method (see below).
Implementation of the layoutSubViews:
- (void) layoutSubviews
{
[super layoutSubviews];
//Resize the UITableView containing all the rows (i.e. it should not scroll within the tableview)
UITableView *bt = [self.subviews objectAtIndex:0];
//1 - Set the height of the table cells
//2 - Calculate the total height for the tableview (i.e.: numberOfRows*rowHeight)
bt.frame = CGRectMake(bt.frame.origin.x, bt.frame.origin.y, bt.frame.size.width, ([bt numberOfRowsInSection:0]*bt.rowHeight));
//Move down the note text view, so that it don't overlaps the table.
UITextView *note = [self.subviews objectAtIndex:1];
note.frame = CGRectMake(note.frame.origin.x, (bt.frame.origin.y + bt.frame.size.height)+15, note.frame.size.width, note.frame.size.height);
//Set the content size to the scroll view.
//Note: If the note is hidden, then we should not include it (the same goes for the padding between note and table)
[self setContentSize: CGSizeMake(self.contentSize.width, bt.frame.size.height + note.frame.size.height)];
UIEdgeInsets insets = UIEdgeInsetsMake(0.0f, 0.0f, /*padding#bottom*/50.0f, 0.0f);
[self setContentInset:insets];
[self setScrollIndicatorInsets:insets];
}
The implementation above solves my problems and renders perfectly. Try to think of it as rendering an recipe where each row in the table view is an ingredient - that is what I'm aiming for.
My app is utilizing the UITabBar and everything renders and behave fine except for the case when I scroll down a bit in the scroll view and then switch to another tab and back. The scroll view is then somehow altered and it is no longer possible to scroll to the top (depending on how much you've scrolled down before switching tab) and is also rendered somewhat strange.
Step 1: Step1.png (see URL below)
Scrolled down to be able to see the textview below expanded tableview
Step 2: step2-switched-back.png (See URL below)
Switching to Second tab and back to First, causing odd rendering behavior and scrolling behavior where it is no longer possible to reach the first row in tableview by scrolling the scroll view.
I've created an example project since I believe the code talks for itself, and I hope someone out there can see through this and point out if I've done something wrong or if there are any way to get round this.
Project & screenshots available at: http://eddiex.se/tmp/demo/
Thanks in advance!
I've now come up with an alternative solution that will give me the same rendering as I was aiming for with having a UITableView expanded inside a UIScrollView.
Solution
I removed the UIScrollView (Editor->Unembed) and set the size of the UITableView to cover whole screen (in UI editor) and I, visually in UI editor, moved the UITextFieldView in to the UITableView (as footer view).
The tiny shadow (gradient view) below the last row in the expanded table was also easy to implement within this solution since no change was needed; UITableView's delegate implemented viewForFooterInSection method, which returned a simple gradient view.