iPhone + UIScrollView - iphone

I have a UIScrollView on screen.
Scroll view contains a view in which are UIButtons.
The problem is that while scrolling the view, if I press any button in between my scroll view will either bounce to top or bottom of the screen, it means it will not remain at place where I have pressed the button.

Take a look at the UIScrollViewDelegate class. You want to disable the buttons while
scrolling begins enable it when the scrolling is done.

I've reproduced the situation you've described in your question (at least I think so): a UIScrollView, a UIView inside the UIScrollView and an UIButton inside the UIView.
I tried taping touching the scroll view and moving my finger across the button and didn't get it to bounce to the top or anything like that. I also tried tapping the button after scrolling the scroll and it had no side effects too.
So my guess is that what you are dealing with is something like this: a UINavigationController with you UIScroll-UIView-UIButton UIViewController as a root view controller; after being tapped the UIButton pushes a new UIViewController to the UINavigationController; then you go back to the previous UIViewController in the UINavigationController stack and this is when you lose you scroll position.
If my guess is right than here's what you need to do.
Before pushing a new UIViewController save you scroll view's current content offset with the following code:
[[NSUserDefaults standardUserDefaults] setObject:NSStringFromCGPoint(theScrollView.contentOffset) forKey:#"ScrollViewLastContentOffset"];
And after you get back to you UIScrollView UIViewController use the following code in you viewWillAppear: method:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults stringForKey:#"ScrollViewLastContentOffset"] != nil) {
theScrollView.contentOffset = CGPointFromString([defaults stringForKey:#"ScrollViewLastContentOffset"]);
[defaults removeObjectForKey:#"ScrollViewLastContentOffset"];
}
}
Even if my guess wrong please do comment on my and Alan's answer and confirm/refute them for us to be able to help you. Would you mind also providing more details on your problem so we wouldn't need to guess what's going on exactly anymore.

If I understand this correctly, your buttons are inside the scrollview? And when pressed you want them to stop the scrolling or move the scrollview to a specific point? Such as the top or the bottom? If so, use myScrollView.contentOffset = CGPointMake(0, 0); and specify the x and y values you need. So (0, 0) in that example would be the top left.
You might also want to try [myScrollView setContentOffset:CGPointMake(0, 0) animated:YES]; if you want to animate this transition.

Related

Best way to create a menu that scrolls up from the bottom?

I'm fairly new to iPhone programming, and I'm making a fairly basic app. Anyway, I just want to know how to go about creating a menu that scrolls up from the bottom (beginning at the top of the tab bar) that displays a few options. I've attached a picture that better helps portray what I mean. I'm assuming I should create some sort of subview and then add some animation to it, but I'd like to get your advice on the best way to start.
Thanks
The simplest solution would be to use a UIActionSheet, unmodified.
If that's not what you're looking for, using a modal view controller is always an option.
If, however, you want something that won't cover the entire screen, and can have a custom look, the basic idea is this:
Create your view with a frame that's just at the bottom of your main view (e.g. at 0,406).
Optionally disable user interaction with the main view
Use UIView animations to move it up
The code would look something like this:
UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height, self.view.bounds.size.width, kCustomViewHeight)];
[self.view addSubview:customView];
[UIView animateWithDuration:0.2 animations:^{
CGRect f = customView.frame;
f.origin.y -= kCustomViewHeight;
customView.frame = f;
}];
I didn't do it myself, but saw a cool source-code, where it was performed with the helped of UIScrollView (by default positioned "below" the screen), containing UITableView and a button (wich was shown at the bottom of the screen). ScrollView was getting a command to move after the press of this button (in IBAction of it).
Being shorter, try to look at UIScrollView (play with UITableView in it).
I've done something similar, popping up some object from the bottom of the screen by subclassing UIActionSheet and adding my own controls as subviews. I imagine a similar method could be used in your situation.
Create a subclass of UIActionSheet implementing the UITableViewDelegate and UITableViewDataSource protocols. In the - (id)init method of that class, add a UITableView as a subview with self as the delegate and data source.
Then from the main view, call your custom UIActionSheet with showInView:.
Use delegates to call back to the main view to let it know when the user selects an option, then use [myCustomMenu dismissWithClickedButtonIndex:0 animated:YES] to hide the menu.

Detecting touches in superview of UITableView when it would bounce if bounces weren't = NO

I've got a UITableView in a UIView subclass in a UIView subclass. I want, instead of the cells "bouncing" down when the the top of the table view is dragged down, the whole UITableView and its containing UIView to be dragged down.
I use the following to stop it from "bouncing" at the top of the UITableView.
-(void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView.contentOffset.y == 0) {
tableView.bounces = NO;
}
}
-(void) scrollViewDidScroll:(UIScrollView *)scrollView {
tableView.bounces = YES;
}
I thought then it might pass touch events for downward swipes up the responder hierarchy. Alas, no. How can I best capture the touch events in my superview, which then moves the UIView containing the UITableView down?
Edit: Just to clarify, here's an example of similar functionality in an existing app: In Twitter's iPad app, if you are zoomed in the browser, with the left edge of the page touching the left edge of the UIWebView, then if you drag from left to right over the web view you end up dragging the web view. That's pretty much what I'm going for, just vertically and with a UITableView.
It sounds as if you are trying to implements scrolling of an entire view that contains a table. If that's the case, may I suggest adding the other portions of the view either as cells in the table, or as a subview in the table.

Automatic scroll to top doesn't work in UITableView

usually when tapping the top of the screen the tableview scrolls all the way to the top. For some reason this doesn't work in one of my view controllers.
The hirarchy is as follows:
UIView
-> WebView
-> TableView
----->SearchBar
SearchDisplayController.
I think I have everything hooked up correctly (datasource, delegate, ...). I have a similar view controller where everything works. The only difference seems to be WebView, which is missing in the view controller where the tap-and-scroll-to-top works...
Any ideas?
Best regards,
Sascha
You have probably added some view to your view hierarchy that is a UIScrollView or contains scroll views (e.g. UIWebView or UITextView). When you tap the status bar, iOS searches for the topmost scrollview with scrollsToTop set to YES and scrolls to its top.
Add this to your class:
- (void) disableScrollsToTopPropertyOnAllSubviewsOf:(UIView *)view {
for (UIView *subview in view.subviews) {
if ([subview isKindOfClass:[UIScrollView class]]) {
((UIScrollView *)subview).scrollsToTop = NO;
}
[self disableScrollsToTopPropertyOnAllSubviewsOf:subview];
}
}
…and call
[self disableScrollsToTopPropertyOnAllSubviewsOf:myAddedSubview];
to solve the problem and let your table view scroll to top again.
You need to disable scrollsToTop on all but one of the scrollable views. Since UITableView is a descendant of UIScrollView, it's easy to do. However, if you want to disable it on the webview, it's a little trickier. This seems to work:
[webView.subviews objectAtIndex:0].scrollsToTop = NO;
But that is a little iffy since it's poking around in undocumented parts of the webview. It may stop working if Apple decides to rearrange the subviews.
Don't know what Apple were smoking but by design, the behaviour on iPhone is distinctly different from iPad. Apple's documentation does include a "Special Condsideration" note as follows...
On iPhone, the scroll-to-top gesture has no effect if there is more
than one scroll view on-screen that has scrollsToTop set to YES.
So on iPhone you have to ensure that there is only ever one UIScrollView (or UITableView/UICollectionView/UIWebView etc) that has its scrollsToTop = YES.
Since scrollsToTop defaults to YES you have to explicitly set scrollsToTop = NO for the UIScrollView's you do not want to scroll when the user taps the status bar.
Apparently if you have more than one scrolling view (in your case the WebView and TableView) in a view controller, the "tap status bar to scroll to top" is disabled.
http://twitter.com/drance/status/2448035250438144
(Matt Drance is a former iPhoneOS developer evangelist and current iOS development rockstar)
Swift variant of opyh's answer. Upvote him!
func disableScrollsToTopPropertyOnAllSubviewsOf(view: UIView) {
for subview in view.subviews {
if let scrollView = subview as? UIScrollView {
(scrollView as UIScrollView).scrollsToTop = false
}
self.disableScrollsToTopPropertyOnAllSubviewsOf(subview as UIView)
}
}
I just wanted to add to this, if you are using a UITextView, it isn't the subviews that need the scrollsToTop adjusting, it's the actual UITextView, in short you need this code:
[myTextView setScrollsToTop:FALSE];
This should fix the problem (additionally, the 'disableScrollsToTopPropertyOnAllSubviewsOf' function doesn't work on the UITextViews).
Hope this helps someone!
I had this problem with just a tableview in the NIB. Very frustrating until I remembered I was programmatically adding a UITextView as the table view header. scrollsToTop=NO on the UITextView did the trick.
Set scrollsToTop = NO on all scroll views in the view, except for the one you want to scroll to top. That helps the system find the correct scroll view.

MKMapView inside a UIScrollView doesn't move with swipes

I've got a detail view with various labels and such providing information about a place (address, phone, etc.). The information provided is taller than an iPhone screen so they're all in a UIScrollView (itself inside a UIView) that allows you to swipe up and down to see everything.
I also have an MKMapView inside the scrollview. When it's not attached to anything in Interface Builder it moves up and down with the scrollview, as it should, staying in it's correct relative position to the other controls in the scrollview. You can play with the map, zooming and panning it, etc. and it shows your current location by default.
However, as soon as I hook it to an MKMapView variable in IB, the mapview no longer scrolls with the scrollview. Instead it literally just sits in the position it's originally displayed in (bottom of the view, with a little of the map hidden below the bottom of the view) and the scrollview scrolls up and down behind it.
What's happening here? I've tried changing a bunch of the mapview's and scrollview's properties in IB, but it has no effect. The only thing I haven't tried is just creating the mapview entirely in code, but that doesn't seem like an obvious solution.
EDIT: Sorry to everyone about the expired bounty. I got hung up in other areas of the project and couldn't get back here until now. I didn't know it would expire.
FURTHER EDIT: Well, I figured out my problem. For reasons completely unknown to me I had
[self.view addSubview:mapView];
in the viewcontoller's ViewDidLoad. Once it was hooked up then that line of code would (obviously) make the map a subview of my of view, effectively yanking it out of the scrollview.
Stupid mistake, sorry to have wasted your time (and the bounty). I'll delete this question after I think the answerers have had a chance to see the result.
Looking like as you are using the ScrollView,you need to scrolling facility in your DetailView.
Instead of using the ScrollView ,I had an alternative of this ....
You can try your hard luck by using the TableView instead of ScrollView.
Just take all the labels and mapView in a single View and then put that view in the header of the TableView.
like this :
UITableView
--> View
------>All Labels // Inside the singleView
------>MKMApView // At bottom of the View
Still You can play with the map, zooming and panning it, etc. and it will show your current location by default.
Hope this alternative can solve your problem.......
All the Best
If hooking up an outlet in IB is breaking an otherwise working view, you might be able to try this to locate the view at runtime:
- (UIView *) findClass:(Class) aClass inView:(UIView *) aSuperview {
for ( UIView *view in aSuperview.subviews ) {
if ( [view isKindOfClass: aClass] ) break;
if ( ( view = [self findClass: aClass inView: aSuperview] ) ) break;
}
return view;
}
- (void) viewDidLoad {
MkMapView *map = [self findClass: [MkMapView class] inView: self.view];
}
I figured out my problem. For reasons completely unknown to me I had
[self.view addSubview:mapView];
in the viewcontoller's ViewDidLoad. Once it was hooked up then that line of code would (obviously) make the map a subview of my of view, effectively yanking it out of the scrollview.
Do you have setContentSize property set to the content's size in the viewDidLoad method of the UIViewController?

Anchor a UIView

I have a UITableViewController inside of a UINavigationController.
I want to have a UIView appear over the top of the table view but not susceptible to the scrolling of the table view.
I.e. if the table view is scrolled, the UIView should remain in the same position relative to the screen, rather than relative to the table view. It should appear anchored in a certain position.
What is the best way to achieve this?
EDIT: To clarify, the view should float transparently over the top of the table view.
Many thanks!
I also wanted to have a floating UIView over my tableView.
So, within my RootViewController (which is a UITableViewController), this worked for me
- (void)viewDidLoad {
/* mylabel is a UILabel set in this class */
[self.mylabel setUserInteractionEnabled:NO];
/* navigationController comes from higher up in the navigation chain */
[self.navigationController.view addSubview:self.mylabel];
}
Similar to what Peter said, create a UIView that will contain both the TableView and the subclassed UIView. Such as:
UIView *view = [[UIView alloc] initWithFrame:frame]; // Define frame as you like
[view addSubview:myTableView]; // This is the reference to your tableView
[view addSubview:myAnchoredView]; // This is the reference to your UIView "floating" subclass
You will also need to turn off user interaction for your floating view. I don't know if this will specifically pass the touches to the underlying UIView's or not though:
[myAnchoredView setUserInteractionEnabled:NO];
If this is blocking touches to your tableView, you may need to pass the reference to your tableView to the anchored view at initialization, then pass the touch events along. You can do this by overriding the touch response methods in UIResponder. (If there is a better way, someone please speak up.)
Do you mean the anchored view should appear transparent over the UITableView, or just above, i.e. anchored view uses top 20% of the available space, table view uses the rest?
In any case, create a UIView containing the anchored view and the table view. If you want the anchored view transparent over the table view, it's a bit tricky, because to scroll the table view, touches have to pass through the anchored view.
Add the surrounding view's view controller to the navigation controller instead of just the tableview.
I investigated how UIScrollView keeps its scrollIndicator above the UIScrollView's content and yet unmoving by examining a UIScrollView in the debugger.
The scrollIndicators are UIImageViews. And you can see they are direct descendants of the UIScrollView itself. You can also see that any scrolled content is also a direct descendent. So how is it that the scroll indicators don't move?
I tried updating the position of my static content constantly in - (void)scrollViewDidScroll:(UIScrollView *)scrollView this, surprisingly, works. I'm not sure if it is how UIScrollView itself does it, but without some private magic, it must be something like this.