With iOS < 6.0 we were able to re-position the "Google" link over map view (by browsing the subviews of map view). Now with iO6, there's a "legal" link and this is a MKAttributeLabel. A private class that we can't manipulate ...
My problem is that I must add a footer subview to my map and it'll hide the legal link ...
How can I solve this problem without any App Store rejection ?
Can I create another legal button my self and add it where I want in my map view ?
I have no idea what I'm able to do...
There's a few answers recommending you move the legal label in the viewDidAppear of your view controller, however this doesn't work if you then resize your map view (like I am).
The best way is to subclass the MKMapView and override the layoutSubviews method. In my example I just needed to nudge the legal label above a semi-transparent toolbar.
-(void)layoutSubviews
{
[super layoutSubviews];
UILabel *legalLabel;
for(UIView *view in self.subviews)
{
if([view isKindOfClass:[UILabel class]])
{
legalLabel = (UILabel *)view;
break;
}
}
legalLabel.center = CGPointMake(legalLabel.center.x, self.bounds.size.height - 55.0f);
}
Does the footer view have to be inside the map boundary, why not put the map and the footer into the same super view?
Related
I have an UITableView inside an UIScrollView and I want that the user will be able to scroll just when he touches the tableView (I have a map in the background and I want that the user will be able to integrate with the map when the tableView doesn't covers all of it.
I've tried to set scrollViewHeightConstraint.constant to scrollView.contentOffset.y on -(void)scrollViewDidScroll:(UIScrollView *)scrollView but it didn't work, part of the map is still not touchable. Can anyone give me an advice what should I do? Thanks!
Screenshot: Screenshot
OK. I think I understand what you're trying to do. Here is the solution I have come up with. Let me know if this works for you or not.
First off, you need to set the contentInset of the table view in your view controller (or if it's possible in the storyboard, set it there. Not sure if it is though?). It's a simple one-liner in viewDidLoad or viewWillAppear:, like so:
self.tableView.contentInset = UIEdgeInsetsMake(400.f, 0.f, 0.f, 0.f);
The 400.f is just saying start the table view's content 400 points from the top. You can set it to whatever number you'd like or, if you know you want it 200 points from the bottom, do something like this:
self.tableView.contentInset = UIEdgeInsetsMake(self.view.bounds.size.height - 200.f, 0.f, 0.f, 0.f);
With your content situated OK, now you need to create a subclass of UITableView to use as your table view's class. This class should override one method: hitTest:withEvent:. You can set which class you're using for your table view in your storyboard (click on the table view, then go to the Identity Inspector), or you can just change it in your view controller if you're not using storyboards.
In the method you're overriding, you're checking to see if where the user has touched the screen is above the contentInset or not (which works for your design). For a more complex design, you'd need some more robust checking.
This should allow the MKMapView to intercept the scroll events from the table view (which is returning nil and disabling scrolling).
Here is the UITableView subclass (you can name it whatever you want):
EmbeddedTableView.h
#import <UIKit/UIKit.h>
#interface EmbeddedTableView : UITableView
#end
EmbeddedTableView.m
#import "EmbeddedTableView.h"
#implementation EmbeddedTableView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (point.y < 0.f) {
self.scrollEnabled = NO;
} else {
self.scrollEnabled = YES;
return [super hitTest:point withEvent:event];
}
return nil;
}
#end
When a UITextField is added to a UIScrollview the scroll view automatically adjusts its contentOffset so that the view will not be obscured by the keyboard.
I have a custom UIControl which also presents a keyboard when it becomes the first responder by assigning its inputView property. The same scrolling behavior does not work. Is there a way to configure a UIControl such that a scroll view will keep it visible when the keyboard is presented?
My guess is that it could be possible by overriding a property defined in one of the protocols UITextField and other classes which this behavior conform to. But these can be a bit of a maze. Also note, the issue here has nothing to do with the scroll view's contentInset property. The scroll view can scroll to show the custom control, it just doesn't do it automatically when the control becomes the first responder.
It looks like this is handled by an internal private method that Apple utilizes [UIFieldEditor scrollSelectionToVisible] as noted on this blog: http://sugarrushva.my03.com/712423-disable-uiscrollview-scrolling-when-uitextfield-becomes-first-responder.html
It appears to do this by stepping back up through the view hierarchy and if it finds a parent UIScrollView, it scrolls the view to bring the UITextField into visible view. You'll need to implement the scrolling manually on your custom control when it becomes first responder, or handle it by introspecting the parent views.
I was pointed in the right direction by #markflowers.
Based on that, here's what I've written into the control to get the desired behavior:
- (BOOL)becomeFirstResponder {
if ([super becomeFirstResponder]) {
[self scrollParentViewToFrame];
return YES;
}
return NO;
}
- (void)scrollParentViewToFrame {
UIScrollView *scrollView = self.parentScrollView;
CGRect frame = [scrollView convertRect:self.bounds fromView:self];
[self.parentScrollView scrollRectToVisible:frame animated:YES];
}
- (UIScrollView *)parentScrollView {
return (UIScrollView *) [self closestParentWithClass:[UIScrollView class]];
}
Note that the frame attribute is not used in case the control is not a direct descendant of the scroll view. Instead convert the bounds to the scroll view's coordinate space.
The scroll adjustment is also needs to be performed after [super becomeFirstResponder] is called for it to interact properly with keyboard notifications that are being used to adjust the insets of the scroll view.
I defined the method to search for the closest parent scroll view in a UIView category which made it easier to recursively search up the hierarchy.
- (UIView *)closestParentWithClass:(Class)class {
if ([self isKindOfClass:class]) {
return self;
}
// Recursively searches up the view hierarchy, returns nil if a view
// has no superview.
return [self.superview closestParentWithClass:class];
}
I have the following hierarchy:
view
scrollview(with scroll and zoom enabled)
containerview
imageview (with a map)
views for buttons
views for navigation panel
Over the image view I have to put some point of interest (depending on the category the user selects):
The problems are:
if i attach them to the container view, they getting the correct position is very easy,
but when zooming they will zoom with the image, and they should keep the same size (1)
if i attach them to the scrollview I don’t see nothing..
If I attach them to the main view, I have two problems:
1 to find their hierarchy position in between scrollview and view for buttons, etc (they get at the top of the application and should be just over the map but below the control panels, buttons, etc
2 to coordinate with the zoom and scroll- done with convertPoint:toView
If I add a view (sibling of container view under the scrollview) they show fine, but I have no user input on them (They don’t receive
touches)
If I convert that sibling subview to a CALayer, I don’t get the sublayers (POIs) to shown.
(1) Ive tried to subclass container view and override setTransform by applying CGAffineTransform invertedTransform = CGAffineTransformInvert(transform); but I had problems passing it an array with views references to it
What would you recommend to do in this case?
Thank you very much!
S.
I've finally added subviews to containerView and I've subclassed it:
- (void)setTransform:(CGAffineTransform)transform
{
[super setTransform:transform];
CGAffineTransform invertedTransform = CGAffineTransformInvert(transform);
for (UIView *view in self.subviews)
{
if ([view isKindOfClass:[MarkerClass class]]) {
[view setTransform:invertedTransform];
}
}
}
The only problem is that before I zoom the containerView, a short sized version of the markers appear on the screen.
After passing some time, I've found I would override addSubview too, to get an initial transform in this way:
-(void)addSubview:(UIView *)view {
[super addSubview:view];
if ([view isKindOfClass:[MarkerClass class]]) {
CGAffineTransform invertedTransform = CGAffineTransformInvert(self.transform);
[view setTransform:invertedTransform];
}
}
I'm currently coding a iOS apps that contains a scrollview in the mainview. When the user want to reset the scrollview I'm running this method:
-(void)clearFlightTimer{
// Removing object (button, label and image) from subview
// *** KNOWN BUG WHEN REMOVING UIImageView OBJECT IT'S ALSO REMOVING THE SCROLLBAR IMAGE ***
for(UIView *subview in [self.flightViewScrollView subviews]) {
if([subview isKindOfClass:[UIButton class]] || [subview isKindOfClass:[UILabel class]] || [subview isKindOfClass:[UIImageView class]]) {
[subview removeFromSuperview];
} else {
// Do nothing - not a UIButton or subclass instance
}
}
// Reloading the View
[self viewDidLoad];
[self viewWillAppear:(YES)];
}
The issue is that the scrollbar seem to be part of UIImageView class and this method remove it. How could I keep the scrollbar image when removing all object from this subview? Is there a way to code the scrollbar indicator back programmatically?
Thanks!
Hope that I'm clear!
The simplest and most fool proof way is probably to add an intermediate subview of the scroll view that contains your actual content. This new container view's subviews can be iterated over and removed as you need. Alternatively, if you know the scroll view is in an empty state, you might be able to iterate over subviews of the scrollview and store all image views that exist when empty - those views will be the ones you shouldn't mess with.
I have an idea for an ios5 navigation I'm doing on an app and I thought it wise to get some constructive criticism from SOF about my idea.
Idea:
UIView containing 6 or so buttons stacked vertically
UIButtons have a selected state.
Buttons static/global keeps track of last touched button and always resets the last touched button when a new UIButton is touched.
Question:
Can you read and access the children of the UIView?
eg. (pseudocode)
for (i in [myView children]) {
[[myView getChildAt:i] doSomethingToThisButton];
}
Thanks all!
Yes. Here's the non-pseudocode (well, mostly):
for (UIView *subview in [myView subviews]) {
[subview doSomethingToThisButton];
}
Or, if you prefer
for (int i = 0; i < [myView.subviews count]; i++) {
[[myView.subviews objectAtIndex:i] doSomethingToThisButton];
}
Don't make your last touched button a static variable because then you can only have one such control in your whole app. Make a UIView subclass to act as the container for your buttons, and have the last selected view be a property of that class.
You may also want to make your containing view a subclass of UIControl instead of UIView, then you can make it send events and bind to it using drag and drop in interface builder, just like a regular control (e.g. a button).