As i want to implement the custom call out in the mkmapview i am using these classes CalloutMapAnnotationView.h and CalloutMapAnnotationView.m
I have extracted these classes from the following links
https://github.com/asalom/Custom-Map-Annotation-Callouts/blob/master/Classes/CalloutMapAnnotationView.h
https://github.com/asalom/Custom-Map-Annotation-Callouts/blob/master/Classes/CalloutMapAnnotationView.m
These work fine in ios5 but in ios6 when i am clicking on the call out the map view is moving and call out is not showing correctly as shown in the below figures while i was zooming also its not coming correctly i have tried several ways to get rid out of this problem by checking the version of os and tried to change the some of the methods in the classes but of not use.
After implementing these in ios5 map view coming like this
In Ios6 This one not coming properly as like in ios5. for example
I too have faced the same problem in iOS 6 using these classes. These changes worked for me:
1) Change in this method:
This method you will implement in your map view class
(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView*)view
as following order of lines
self.selectedAnnotationView = view;
[self.mapView addAnnotation:self.calloutAnnotation];
2) In CalloutMapAnnotationView.m file
- (void)adjustMapRegionIfNeeded in this method first 5 lines like bellow
CGFloat xPixelShift = 0;
if ([self relativeParentXPosition] < 38) {
xPixelShift = 38 - [self relativeParentXPosition];
} else if ([self relativeParentXPosition] > self.frame.size.width - 38) {
xPixelShift = (self.frame.size.width - 38) - [self relativeParentXPosition];
}
3) In same class CalloutMapAnnotationView.m - (void)drawRect:(CGRect)rect in this method afterCGFloat parentX = [self relativeParentXPosition];
line and above rect = self.bounds; this line
add following lines
if ([self relativeParentXPosition] < 38) {
parentX = 38;
} else if ([self relativeParentXPosition] > self.mapView.frame.size.width - 38) {
parentX = [self relativeParentXPosition]-25;
}
4) In same class CalloutMapAnnotationView.m.
- (void)didMoveToSuperview {
[super didMoveToSuperview];
[self.superview bringSubviewToFront:self];
}
You can use directly the above classes and use them they work fine in both iOS 5 & iOS 6.
You should make necessary changes according to your requirements.
just u can add nsstring title and nstring subtitle like that and
_name is what title u want .
- (NSString *)title {
return _name;
}
- (NSString *)subtitle {
return [NSString stringWithFormat:#"%f, %f", _coordinate.latitude, _coordinate.longitude];
}
Related
I have a problem with MKMapView. I add annotations like that:
// set up new points
for(int i = 0; i < [_locations count]; i++) {
PPlace * place = [_locations objectAtIndex:i];
PlaceAnnotation * placeAnnotation = [[PlaceAnnotation alloc] initWithPlace:place];
// if annotation is for currently selected place
placeAnnotation.isCurrent = i == currentIndexPath.row;
[self.mapView addAnnotation:placeAnnotation];
if (placeAnnotation.isCurrent) {
[self.mapView selectAnnotation:placeAnnotation animated:YES];
}
[placeAnnotation release];
}
So I try to display callout bouble immediately after added, not after annotation pin is tapped.
Everything works fine in simulator, also on iPhone 3GS with iOS 4.3.2. However, the callouts do not show on iPhone 4 with iOS 4.1 (they show only after pin is tapped). Any idea how to solve this?
My guess is that you did not assign a value to the title property of your annotation class. Even though you may set canShowCallout to YES, the call out bubble will not show unless you have something in your title.
try adding
placeAnnotation.canShowCallout = YES;
so it looks like:
// set up new points
for(int i = 0; i < [_locations count]; i++) {
PPlace * place = [_locations objectAtIndex:i];
PlaceAnnotation * placeAnnotation = [[PlaceAnnotation alloc] initWithPlace:place];
// if annotation is for currently selected place
placeAnnotation.isCurrent = i == currentIndexPath.row;
[self.mapView addAnnotation:placeAnnotation];
if (placeAnnotation.isCurrent) {
[self.mapView selectAnnotation:placeAnnotation animated:YES];
placeAnnotation.canShowCallout = YES;
}
[placeAnnotation release];
}
hope this helps!
WeSaM
You will need to implement the following method:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
You can find the documentation here
You are calling it at the wrong time. You can't select it until after it has loaded.
Use the delegate method of the MKMapView:
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views
And from inside of that method call:
[yourMapView selectAnnotation: yourAnnotation animated: YES];
Hi i dont know where to look further im totally stuck:
Scenario: When i leave edit mode of my view i delete rows and sections of my table.
The table consits of different custom cells which have animation when entering or leaving
editmode.
Problem: Sometimes and i dont know when, i cant reproduce it, the app crashes with a "
CALayerInvalidGeometry', reason: 'CALayer position contains NaN: [nan 2.03571e-10]" error message.
I looked for hours now but i cant find the error therefor i provide some more code maybe anyone knows where it happens:
I call tis method to leave edit mode of the table in it setupDetailEditMode gets called.
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
if(editing)
{..}
else{
tableShowEditMode = NO;
[self setupDetailEditMode];
[self createPersonTableHeader];
[self clearAllEmptyFieldsFromArray];
[self saveUserProfile];
[super setEditing:editing animated:animated];
[profileTable setEditing:editing animated:YES];
......}
This code is where i delete sections.... some of the sections still have rows which is no problem cause they will be deleted by deleting the section in the following function it crashes last line:
part of function **setupDetailEditMode**:
//Phone Section
if(removedRowsPhone)
{
if(app.isitUserProfile.isitPhone.count == 0)
[sectionOperationIndexSet addIndex:sectionWorked];
sectionWorked++;
}
//Email Section
if(removedRowsEmail)
{
if(app.isitUserProfile.isitEmail.count == 0)
[sectionOperationIndexSet addIndex:sectionWorked];
sectionWorked++;
}
//update table data structur
[self buildCurrentTableStructure];
//delete sections <<<<<**here will crash the app sometimes**
[profileTable deleteSections:sectionOperationIndexSet withRowAnimation:UITableViewRowAnimationFade];
I add a random custom cell i use maybe there is something wrong that causes the crash in layoutsubviews:
- (void) layoutSubviews {
[super layoutSubviews];
if (self.editing)
{
primaryField.enabled = YES;
// (self.editing && !self.showingDeleteConfirmation)
star1Img.frame = CGRectMake(87.0,30.0,20.0, 20.0);
star2Img.frame = CGRectMake(111.0,30.0,20.0, 20.0);
star3Img.frame = CGRectMake(135.0,30.0,20.0, 20.0);
star4Img.frame = CGRectMake(159.0,30.0,20.0, 20.0);
star5Img.frame = CGRectMake(183.0,30.0,20.0, 20.0);
//primaryLabel.frame = CGRectMake(primaryLabel.frame.origin.x, 8.0,180.0 , 17.0);
primaryField.frame = CGRectMake(primaryField.frame.origin.x, 8.0,180.0 , 17.0);
secondaryLabel.frame = CGRectMake(secondaryLabel.frame.origin.x,18.0, 65.0 , 21.0);
}
else
{
primaryField.enabled = NO;
star1Img.frame = CGRectMake(29.0,27.0,10.0, 10.0);
star2Img.frame = CGRectMake(39.0,27.0,10.0, 10.0);
star3Img.frame = CGRectMake(49.0,27.0,10.0, 10.0);
star4Img.frame = CGRectMake(59.0,27.0,10.0, 10.0);
star5Img.frame = CGRectMake(69.0,27.0,10.0, 10.0);
primaryField.frame = CGRectMake(primaryField.frame.origin.x, 14.0,213.0 , 17.0);
secondaryLabel.frame = CGRectMake(14.0,6.0, 65.0 , 21.0);
}
} // layoutSubviews*/
- (void) setStarRating:(int) rating
{
if(rating>=1)
[star1Img setImage:[UIImage imageNamed:#"goldstar20.png"]];
......}
I am very frustrated atm because i cant find this bug it would be very niice if anyone has a hint or find the bug. Anything will help. Thanks
-Bevo
I haven't looked through your code very much, but I had something similar once. There was a view that got released but did want to be painted again - basically a retain thing. Hard to find - or understand - even via profiler. You should check with the profiler if there is a leak anywhere.
I have a requirement where UIPickerView should be customized. The picker view should look something like this:
The application which has customized the pickerView similarly is:
http://itunes.apple.com/us/app/convert-the-unit-calculator/id325758140?mt=8
I have tried removing the default pickerView selection bar by resetting the property showsSelectionIndicator of UIImagePicker and adding a overlay view. But the problem is, the overlay view should be transparent so that the wheel behind it is visible. But the other application somehow does it even though the selection bar is not transparent.
Any ideas on how to achieve this feat?
Thanks and Regards,
Raj
You're going to have to write your own from scratch on this one. UIPickerview isn't customizable. At. All. Sucks, but that's how it is. I'd start out creating a uitableview and layering a frame around it, and trying to mimic uipickerview.
I think you can print the subviews under the picker and modify them.
The UIPickerView create subviews after the data is first loaded.
With performSelecter:WithObject:afterDelay:, you can remove them or insert whatever you need.
- (void)viewDidLoad
{
[super viewDidLoad];
[self refreshClock];
[timePicker_ performSelector:#selector(leakSelf) withObject:nil afterDelay:0];
[self performSelector:#selector(setupTimePicker) withObject:nil afterDelay:0];
}
- (void)setupTimePicker {
[[timePicker_ subviewOfClass:NSClassFromString(#"_UIPickerViewTopFrame")] removeFromSuperview];
CGRect frame = timePicker_.frame;
frame.size.width += 20;
frame.origin.x -= 10;
timePicker_.frame = frame;
}
#implementation UIView(UIViewDebugTool)
- (void)leakSubview:(UIView*)subroot atDepth:(NSUInteger)dep {
DLog( #"trace sub view[%u] %#", dep, [subroot description] );
CALayer* layer = subroot.layer;
for( CALayer* l in layer.sublayers ) {
DLog( #"layer[%u] %#", dep, l );
}
for( UIView* v in subroot.subviews ) {
[self leakSubview:v atDepth:dep+1];
}
}
- (void)leakSelf {
NSUInteger dep = 0;
[self leakSubview: self atDepth:dep];
}
#end
I'm trying to use a UIPicker View with a behavior somehow different of what's usually seen in iPhone code examples.
What I want to do is to allow users to scroll through the picker contents, but not to select a picker's row automatically (using the "didSelectRow" method from picker's delegate). Instead, I want to allow the user to touch the center row of the picker, which gets highlighted, and becomes the selection.
Is there any way to achieve this?
Thanks in advance.
Add a gesture recogniser to the UIPickerView which triggers a target method in your object:
myGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(pickerTapped:)];
[myPicker addGestureRecognizer:myGR];
// target method
-(void)pickerTapped:(id)sender
{
// your code
}
make a new UIControl
same position as the UIPickerView
[yourcontrol setBackGroundColor: [UIColor clearColor]];
make a method
- (IBAction) pickerControlTapped
{
[yourpicker selectRow: rand()% yourpickersize
inComponent: 0
animated: YES];
}
.3. make a connection between 1 and 2
[yourcontrol addTarget: self
action: #selector(pickerControlTapped)
forControlEvents: UIControlEventTouchUpInsied];
Building on Martin Linklater's answer to support tapping on the pickers other rows:
Has some magic numbers but works for me.
- (void) pickerTapped:(UITapGestureRecognizer*)gestureRecognizer
{
CGPoint location = [gestureRecognizer locationInView:self.pickerView];
CGFloat halfViewHeight = self.pickerView.frame.size.height / 2;
NSInteger row = -1;
if (location.y < halfViewHeight - 22
&& location.y > halfViewHeight - 66)
{
row = [self.pickerView selectedRowInComponent:0] - 1;
}
else if (location.y < halfViewHeight + 22
&& location.y > halfViewHeight - 22)
{
row = [self.pickerView selectedRowInComponent:0];
}
else if (location.y < halfViewHeight + 66
&& location.y > halfViewHeight + 22)
{
row = [self.pickerView selectedRowInComponent:0] + 1;
}
if (row >= 0 && row < [self.content count])
{
id element = [self.content objectAtIndex:row];
if (element)
{
[self.pickerView selectRow:row inComponent:0 animated:YES];
// do more stuff
}
}
}
I have a relatively simple solution to this problem that has worked well for me. Using a hidden custom button you can achieve the tap functionality without a gesture recogniser. This solution works for a picker with one component, however I'm sure it could be adapted to work with more.
Firstly add a button, either in the Interface Builder or programatically. Make it hidden and as wide as the picker then place it so that it sits exactly in the centre of the picker and also in front of it in the view hierarchy.
I'm using an IBAction like this to show my picker. However it's really up to you how you show and hide the picker.
- (IBAction)showPicker:(id)sender
{
_picker.hidden = NO;
_buttonPicker.hidden = NO;
}
All the action for choosing the picker value happens in an IBAction for the UIControlEventTouchUpInside event, something like this.
- (IBAction)selectPicker:(id)sender
{
//Hide the button so that it doesn't get in the way
_buttonPicker.hidden = YES;
//Make sure we're within range
NSInteger max = _values.count;
NSInteger row = [_picker selectedRowInComponent:0];
if(row >= 0 && row < max) {
NSString *value = [_values objectAtIndex:row];
//Set the label value and hide the picker
_label.text = value;
_picker.hidden = YES;
}
}
I've slightly modified the code for this answer from working code so apologies if it's broken at all.
There are only 2 delegates for UIPickerView.
UIPickerViewDelegate
UIPickerViewDataSource
So, we can use only 7 methods to control UIPickerView by delegate.
– pickerView:rowHeightForComponent:
– pickerView:widthForComponent:
– pickerView:titleForRow:forComponent:
– pickerView:viewForRow:forComponent:reusingView:
– pickerView:didSelectRow:inComponent:
– numberOfComponentsInPickerView:
– pickerView:numberOfRowsInComponent:
that'all.
In UITableViewDelegate case, there are more methods for UITableView for managing selections.
such as,
– tableView:willSelectRowAtIndexPath:
– tableView:didSelectRowAtIndexPath:
– tableView:willDeselectRowAtIndexPath:
– tableView:didDeselectRowAtIndexPath:
However...
In UIPickerViewDelegate case, there is only 1 method for responding to row selection.
– pickerView:didSelectRow:inComponent:
Does anyone know if its possible to remove the shadow that is placed on the UIWebView window?
Example: http://uploadingit.com/files/1173105_olub5/shadow.png
If its possible how do you do it?
Thanks
This is a cleaner alternative to "Nikolai Krill" solution. This only hides UIImageViews within the UIWebView and not the UIWebBrowserView.
for (UIView *view in [[[webView subviews] objectAtIndex:0] subviews]) {
if ([view isKindOfClass:[UIImageView class]]) view.hidden = YES;
}
Thanks
James
the small for loop is very dangerous because it can crash if apple changes the number of the subviews.
this way it does at least not crash when something changes:
if ([[webView subviews] count] > 0)
{
for (UIView* shadowView in [[[webView subviews] objectAtIndex:0] subviews])
{
[shadowView setHidden:YES];
}
// unhide the last view so it is visible again because it has the content
[[[[[webView subviews] objectAtIndex:0] subviews] lastObject] setHidden:NO];
}
There is a private method with the selector setAllowsRubberBanding: that takes a BOOL value. If passed NO, you will not be able to scroll the web view past the top or bottom of the content area, but will still let you scroll through the web view normally. Unfortunately, this method IS private, and your app will likely not be allowed onto the store if you use it.
You could, however, potentially try and extract the method implementation and bind it to a different selector that you've created, using the dynamic nature of Objective-C's runtime.
Still, the method is private and may no longer exist in future versions of the OS. If you still want to try, here's some sample code that will extract the setAllowsRubberBanding: method implementation and call it for you.
static inline void ShhhDoNotTellAppleAboutThis (UIWebView *webview)
{
const char *hax3d = "frgNyybjfEhooreOnaqvat";
char appleSelName[24];
for (int i = 0; i < 22; ++i)
{
char c = hax3d[i];
appleSelName[i] = (c >= 'a' && c <= 'z') ? ((c - 'a' + 13) % 26) + 'a' : ((c - 'A' + 13) % 26) + 'A';
}
appleSelName[22] = ':';
appleSelName[23] = 0;
SEL appleSEL = sel_getUid(appleSelName);
UIScrollView *scrollView = (UIScrollView *)[webview.subviews objectAtIndex:0];
Class cls = [scrollView class];
if (class_respondsToSelector(cls, appleSEL) == NO)
{
return;
}
IMP func = class_getMethodImplementation(cls, appleSEL);
func(scrollView, appleSEL, NO);
}
Please note that this will probably still get caught by Apple's static analyzer if you choose to submit an app using this code to the AppStore.
Here is a Swift function that gets rid of the shadow in a UIWebView in iOS 9. It’s safer than any alternative I’ve seen on SO because everything in it is in Apple documentation, and it specifically alters the shadow property (as opposed to hiding the entire view or some other property of the view).
func removeShadow(webView: UIWebView) {
for subview:UIView in webView.scrollView.subviews {
subview.layer.shadowOpacity = 0
for subsubview in subview.subviews {
subsubview.layer.shadowOpacity = 0
}
}
}
You can always access the subviews property of a UIView(documentation). Every UIView has a layer property that is a CALayer (documentation). Every CALayer has shadowOpacity (documentation).
Caveats:
You might have to go deeper in navigating the view hierarchy through subviews depending on your situation.
This works as long as you don’t want any shadows anywhere in the web view controller. If you have a view where you want to keep the shadow (other than the default UIWebView shadow), then you could add an if-check to identify that view and not set that view’s layer’s shadowOpacity to zero.
According to Apple “For complex views declared in UIKit and other system frameworks, any subviews of the view are generally considered private and subject to change at any time. Therefore, you should not attempt to retrieve or modify subviews for these types of system-supplied views. If you do, your code may break during a future system update” . . . in other words, UIWebView can change and its not recommended to be digging into these subviews. However, digging into the UIWebView is the only way to get rid of the shadow and this is a relatively safe way to do it.
This can be done without use of private APIs. All you need to do is hide each UIImageView with the shadow in it. Heres the code:
for (int x = 0; x < 10; ++x) {
[[[[[webView subviews] objectAtIndex:0] subviews] objectAtIndex:x] setHidden:YES];
}
Try this
func webViewDidFinishLoad(_ webView: UIWebView) {
for shadowView in self.webView.scrollView.subviews {
if !shadowView.isKind(of: UIImageView.self) {
shadowView.subviews[0].layer.shadowColor = UIColor.clear.cgColor
} else {
shadowView.layer.shadowColor = UIColor.clear.cgColor
}
}
}
Traverse all subviews, the UIImageViews whose image is only 1 pixel wide are shadow images, you can hide them.
- (void)hideShadows {
[webview traverseViewsWithBlock:^(UIView *view) {
UIImageView *imgView = ([view isKindOfClass:[UIImageView class]] ? (UIImageView*)view : nil;
// image views whose image is 1px wide are shadow images, hide them
if (imgView && imgView.image.size.width == 1) {
imgView.hidden = YES;
}
}];
}
traverseViewsWithBlock does what it looks like:
- (void)traverseViewsWithBlock:(void (^)(UIView* view))block
{
block(self);
for (id subview in self.subviews) {
[subview traverseViewsWithBlock:block];
}
}
I looked at the class properties and didn't find anything there but I can think of two "cover up" strategies:
1. You can use another view (parent of the web view) to clip the webview bounds.
2. You can add another view on top of the webview to cover the needed area with a color that matches the background, you can use an uiimage with a transparent area in the center.
By the way I don't like this standard background of the table views :P, but changing it can be a pain in the ass :P
You have to be careful, the scroll indicators are UIImageViews as well.
I'll improve my code, but here's a basic subclassed solution:
http://forrst.com/posts/A_tiny_UIWebView_hack_remove_shadows_from_behi-gzH
The easiest way to hide scroll indicators and transparent the web view here in UIWebView
To remove the scrolls.
for(UIView *view in webView.subviews){
if ([view isKindOfClass:[UIScrollView class]]) {
UIScrollView *sView = (UIScrollView *)view;
//to hide verticalScroller
sView.showsVerticalScrollIndicator = NO;
sView.showsVerticalScrollIndicator = NO;
}
}
What about a category on UIWebView like this:
- (BOOL)showsScrollShadows
{
for(UIImageView *imageView in [self imageViewsWithShadows])
{
if(imageView.hidden)
{
return NO;
}
break;
}
return YES;
}
- (void)setShowsScrollShadows:(BOOL)showsScrollShadows
{
[[self imageViewsWithShadows] makeObjectsPerformSelector:#selector(setHidden:) withObject:#(!showsScrollShadows)];
}
- (NSArray *)imageViewsWithShadows
{
NSArray *potentialShadowImageViews = (self.subviews.count > 0) ? [self.subviews[0] subviews] : nil;
if(potentialShadowImageViews.count > 0)
{
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings)
{
return [evaluatedObject isKindOfClass:[UIImageView class]];
}];
return [potentialShadowImageViews filteredArrayUsingPredicate:predicate];
}
return nil;
}
I've had a look around and can't see anything related to it. Apart from masking it with a view or clipping it somehow, the only thing I can think of is to loop through all of the UIWebView subviews (and sub-subviews etc.) and see if you can see anything there!
I may be wrong, but I think the shadow only shows up when we scroll the webview doesn't it ?
In that case, do you want to prevent the scrolling or really hide the shadow ? I don't know any tips that would hide the shadow. To disable the scrolling, I would setUserInteractionEnabled to NO.
I added a recursive method as a category to the UIView object so that it will do a depth-first walk of the subviews of the method's receiving view, hiding any UIImageView subclasses it finds. It will not crash if there are no subviews. The -apply: method is from BlocksKit. You could rewrite this function not to use it, but the block is applied in parallel to each element of the receiving array, so it's pretty fast.
#implementation UIView (RemoveShadow)
- (void)removeShadow {
if (self.subviews.count == 0 && [self isKindOfClass:[UIImageView class]]) {
self.hidden = YES;
} else if (self.subviews.count > 0) {
[self.subviews apply:^(id sender) {
[(UIView *)sender removeShadow];
}];
}
}
#end
if (UIDevice.currentDevice.systemVersion.intValue < 7)
for (UIImageView *imageView in webView.scrollView.subviews)
if ([imageView isKindOfClass:[UIImageView class]] && imageView.image.size.width == 1)
imageView.hidden = YES;