MKMapView custom callOutView with auto layout iOS 6 vs. iOS7 - ios6

Hej Folks,
my apps crashes on a MKMapView under iOS 6 if I use auto layout for the callout view. With iOS 7 this works fine.
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
CustomMapAnnotation *annotation = (CustomMapAnnotation *)view.annotation;
if([annotation isKindOfClass:[CustomMapAnnotation class]]) {
CustomMapCalloutView *calloutView = [CustomMapCalloutView new];
calloutView.translatesAutoresizingMaskIntoConstraints = NO;
calloutView.titleLabel.text = annotation.titleText;
calloutView.subTitleLabel.text = annotation.subTitleText;
calloutView.distanceTextLabel.text = annotation.distanceText;
[view addSubview:calloutView];
NSDictionary *viewsDictionary = #{#"callOutView": calloutView};
NSArray *hConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[callOutView(150)]" options:0 metrics:nil views:viewsDictionary];
NSArray *vConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[callOutView(50)]" options:0 metrics:nil views:viewsDictionary];
NSLayoutConstraint *xConstraint = [NSLayoutConstraint constraintWithItem:calloutView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];
NSLayoutConstraint *yConstraint = [NSLayoutConstraint constraintWithItem:calloutView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0];
[view addConstraint:xConstraint];
[view addConstraint:yConstraint];
[view addConstraints:hConstraints];
[view addConstraints:vConstraints];
}}
The console shows the following error:
* Assertion failure in -[MKAnnotationView layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2380.17/UIView.m:5776 2013-10-27
13:39:18.519 PartySmarty[9825:907] * Terminating app due to uncaught
exception 'NSInternalInconsistencyException', reason: 'Auto Layout
still required after executing -layoutSubviews. MKAnnotationView's
implementation of -layoutSubviews needs to call super.'
Can somebody give me a hint where the problem is? The CustomCalloutView works also with auto layout and I don't overwrite layoutSubviews within.

This SO Answer points me in the right direction. I create a category on the MKAnnotationView, override the layoutSubviews method and call the super method.
This works also on iOS 7.

Related

Get the name of UIImage in order to check if the image is in an Array of images - Swift [duplicate]

Is it possible to read the name of an UIImageView's UIImage
that's presently stored in the UIImageView?
I was hoping you could do something kind of like this, but haven't figured it out.
NSString *currentImageName = [MyIImageView getFileName];
you can use setAccessibilityIdentifier method for any subclass of UIView
UIImageView *image ;
[image setAccessibilityIdentifier:#"file name"] ;
NSString *file_name = [image accessibilityIdentifier] ;
Nope. You can't do that.
The reason is that a UIImageView instance does not store an image file. It stores a displays a UIImage instance. When you make an image from a file, you do something like this:
UIImage *picture = [UIImage imageNamed:#"myFile.png"];
Once this is done, there is no longer any reference to the filename. The UIImage instance contains the data, regardless of where it got it. Thus, the UIImageView couldn't possibly know the filename.
Also, even if you could, you would never get filename info from a view. That breaks MVC.
No no no… in general these things are possible. It'll just make you feel like a dirty person. If you absolutely must, do this:
Create a category with your own implementation of +imageNamed:(NSString*)imageName that calls through to the existing implementation and uses the technique identified here (How do I use objc_setAssociatedObject/objc_getAssociatedObject inside an object?) to permanently associate imageName with the UIImage object that is returned.
Use Method Swizzling to swap the provided implementation of imageNamed: for your implementation in the method lookup table of the Objective-C runtime.
Access the name you associated with the UIImage instance (using objc_getAssociatedObject) anytime you want it.
I can verify that this works, with the caveat that you can't get the names of UIImage's loaded in NIBs. It appears that images loaded from NIBs are not created through any standard function calls, so it's really a mystery to me.
I'm leaving the implementation up to you. Copy-pasting code that screws with the Objective-C runtime is a very bad idea, so think carefully about your project's needs and implement this only if you must.
There is no native way to do this; however, you could easily create this behavior yourself.
You can subclass UIImageView and add a new instance variable:
NSString* imageFileName;
Then you could override setImage, first setting imageFileName to the filename of the image you're setting, and then calling [super setImage:imageFileName]. Something like this:
-(void) setImage:(NSString*)fileName
{
imageFileName = fileName;
[super setImage:fileName];
}
Just because it can't be done natively doesn't mean it isn't possible :)
if ([imageForCheckMark.image isEqual:[UIImage imageNamed:#"crossCheckMark.png"]]||[imageForCheckMark.image isEqual:[UIImage imageNamed:#"checkMark.png"]])
{
}
Nope. No way to do that natively.
You're going to have to subclass UIImageView, and add an imageFileName property (which you set when you set the image).
Neither UIImageView not UIImage holds on to the filename of the image loaded.
You can either
1: (as suggested by Kenny Winker above) subclass UIImageView to have a fileName property or
2: name the image files with numbers (image1.jpg, image2.jpg etc) and tag those images with the corresponding number (tag=1 for image1.jpg, tag=2 for image2.jpg etc) or
3: Have a class level variable (eg. NSString *currentFileName) which updates whenever you update the UIImageView's image
Or you can use the restoration identifier, like this:
let myImageView = UIImageView()
myImageView.image = UIImage(named: "anyImage")
myImageView.restorationIdentifier = "anyImage" // Same name as image's name!
// Later, in UI Tests:
print(myImageView.restorationIdentifier!) // Prints "anyImage"
Basically in this solution you're using the restoration identifier to hold the image's name, so you can use it later anywhere. If you update the image, you must also update the restoration identifier, like this:
myImageView.restorationIdentifier = "newImageName"
I hope that helps you, good luck!
This code will help you out:-
- (NSString *)getFileName:(UIImageView *)imgView{
NSString *imgName = [imgView image].accessibilityIdentifier;
NSLog(#"%#",imgName);
return imgName;
}
Use this as:-
NSString *currentImageName = [self getFileName:MyIImageView];
In short:
uiImageView.image?.imageAsset?.value(forKey: "assetName")
UIImage has an imageAsset property (since iOS 8.0) that references the UIImageAsset it was created from (if any).
UIImageAsset has an assetName property that has the information you want. Unfortunately it is not public, hence the need to use value(forKey: "assetName"). Use at your own risk, as it's officially out of bounds for the App Store.
Yes you can compare with the help of data like below code
UITableViewCell *cell = (UITableViewCell*)[self.view viewWithTag:indexPath.row + 100];
UIImage *secondImage = [UIImage imageNamed:#"boxhover.png"];
NSData *imgData1 = UIImagePNGRepresentation(cell.imageView.image);
NSData *imgData2 = UIImagePNGRepresentation(secondImage);
BOOL isCompare = [imgData1 isEqual:imgData2];
if(isCompare)
{
//contain same image
cell.imageView.image = [UIImage imageNamed:#"box.png"];
}
else
{
//does not contain same image
cell.imageView.image = secondImage;
}
You can use objective c Runtime feature for associating imagename with the UImageView.
First import #import <objc/runtime.h> in your class
then implement your code as below :
NSString *filename = #"exampleImage";
UIImage *image = [UIImage imagedName:filename];
objc_setAssociatedObject(image, "imageFilename", filename, OBJC_ASSOCIATION_COPY);
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
//You can then get the image later:
NSString *filename = objc_getAssociatedObject(imageView, "imageFilename");
Hope it helps you.
Get image name Swift 4.2
There is a way if you want to compare button image names that you have in assets.
#IBOutlet weak var extraShotCheckbox: UIButton!
#IBAction func extraShotCheckBoxAction(_ sender: Any) {
extraShotCheckbox.setImage(changeCheckBoxImage(button: extraShotCheckbox), for: .normal)
}
func changeCheckBoxImage(button: UIButton) -> UIImage {
if let imageView = button.imageView, let image = imageView.image {
if image == UIImage(named: "checkboxGrayOn") {
return UIImage(named: "checkbox")!
} else {
return UIImage(named: "checkboxGrayOn")!
}
}
return UIImage()
}
Swift 3
First set the accessibilityIdentifier as imageName
myImageView.image?.accessibilityIdentifier = "add-image"
Then Use the following code.
extension UIImageView {
func getFileName() -> String? {
// First set accessibilityIdentifier of image before calling.
let imgName = self.image?.accessibilityIdentifier
return imgName
}
}
Finally, The calling way of method to identify
myImageView.getFileName()
I have deal with this problem, I have been solved it by MVC design pattern, I created Card class:
#interface Card : NSObject
#property (strong,nonatomic) UIImage* img;
#property (strong,nonatomic) NSString* url;
#end
//then in the UIViewController in the DidLoad Method to Do :
// init Cards
Card* card10= [[Card alloc]init];
card10.url=#"image.jpg";
card10.img = [UIImage imageNamed:[card10 url]];
// for Example
UIImageView * myImageView = [[UIImageView alloc]initWithImage:card10.img];
[self.view addSubview:myImageView];
//may you want to check the image name , so you can do this:
//for example
NSString * str = #"image.jpg";
if([str isEqualToString: [card10 url]]){
// your code here
}
use below
UIImageView *imageView = ((UIImageView *)(barButtonItem.customView.subviews.lastObject));
file_name = imageView.accessibilityLabel;
The code is work in swift3 - write code inside didFinishPickingMediaWithInfo delegate method:
if let referenceUrl = info[UIImagePickerControllerReferenceURL] as? NSURL {
ALAssetsLibrary().asset(for: referenceUrl as URL!, resultBlock: { asset in
let fileName = asset?.defaultRepresentation().filename()
print(fileName!)
//do whatever with your file name
}, failureBlock: nil)
}

iOS 6 slide push controller animation in iOS 7

I am creating an application and wants to use same iOS 6 style right to left slide push controller animation in iOS 7. iOS 7 changed the default controller push animation.
How can i get previous animation?
If you found this question duplicate, then please give me the answer link before any comment.
Thanks for your time.
Let me clear one more thing.
I have controller that have a custom view of size (320x400) in iOS 7. I use a UINavigationController (_homeInnerNavigationCtrl) that will add more controllers init and this _homeInnerNavigationCtrl.view is subView in custom view. So when i try to use this code:
CATransition *transition = [CATransition animation];
transition.duration = 0.5;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
[currentViewCtrl.view.layer addAnimation:transition forKey:nil];
[currentViewCtrl presentViewController:targetCtrl animated:NO completion:^{
[_homeInnerNavigationCtrl pushViewController:targetViewCtrl animated:NO];
}];
It gives me crash:
Terminating app due to uncaught exception 'UIViewControllerHierarchyInconsistency', reason: 'adding a root view controller as a child of view controller:'
Hope you will understand the description. Thanks.
While presenting controller use this code:
CATransition *transition = [CATransition animation];
transition.duration = 0.5;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
[self.view.window.layer addAnimation:transition forKey:nil];
[self presentModalViewController:viewController animated:NO];
and when you dismiss, use this code:
CATransition *transition = [CATransition animation];
transition.duration = 0.5;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
[self.view.window.layer addAnimation:transition forKey:nil];
[self dismissModalViewControllerAnimated:NO];

iOS 6 Auto Layout Constraints Error: "Something is nil"

I am trying to use constrains in iOS 6 and want to align a label to another. One label and the constrain are created dynamically on a tap on a button. This is a very simple example but i get a wired error and i don't know what's the cause.
The code:
- (IBAction)addToLog:(UIButton *)sender {
UILabel *labelLastTime = [[UILabel alloc]initWithFrame:CGRectMake(20, 359, 92, 42)];
labelLastTime.text = [NSString stringWithFormat:#"%#kg x %#", self.selectedPickerWeight, self.selectedPickerRepetitions];
labelLastTime.font = [UIFont boldSystemFontOfSize:17.0];
labelLastTime.textAlignment = NSTextAlignmentCenter;
labelLastTime.textColor = [UIColor colorWithRed:133.0/255.0 green:133.0/255.0 blue:133.0/255.0 alpha:1.0];
labelLastTime.backgroundColor = [UIColor colorWithRed:228.0/255.0 green:228.0/255.0 blue:228.0/255.0 alpha:1.0];
labelLastTime.layer.borderColor = [UIColor colorWithRed:185.0/255.0 green:185.0/255.0 blue:185.0/255.0 alpha:1.0].CGColor;
labelLastTime.layer.borderWidth = 1;
labelLastTime.layer.cornerRadius = 5;
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:labelLastTime];
NSLayoutConstraint *cn = [NSLayoutConstraint constraintWithItem:labelLastTime
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.labelTodayVsLastTime
attribute:NSLayoutFormatAlignAllBottom
multiplier:1.0
constant:5.0];
[self.view addConstraint:cn];
}
The Error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to create description in descriptionForLayoutAttribute_layoutItem_coefficient. Something is nil'
The error occurs when i apply the constrain, not when i set it up. I have set a breakpoint just before
[self.view addConstraint:cn]
and when i inspect the result, i get nil values for this 4
container, markerAndPositiveExtraVar, negativeExtraVar and _flange
NSLayoutConstraint *cn = [NSLayoutConstraint constraintWithItem:labelLastTime
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.labelTodayVsLastTime
attribute:NSLayoutFormatAlignAllBottom
multiplier:1.0
constant:5.0];
I had the same problem with a slightly different constraint definition and the cause was an Xcode autocomplete snafu.
Have a look at the second attribute: line. You probably wanted something like attribute: NSLayoutAttributeBottom.
NSLayoutFormatAlignAllBottom is for building an NSLayoutFormatOptions bitmask to be given to something like constraintsWithVisualFormat:options:metrics:views:.

Strange crash when I try to access to uibutton's titleLabel property (xcode 4.5 and IOS sdk 6.0)

I found another annoying bug with xcode 4.5 and sdk 6.0 :
when I run the following code :
UIColor *newcolor = [UIColor colorWithCIColor:[CIColor colorWithString:#"1 1 1 1"]];
[button setTitleColor:newcolor forState:UIControlStateNormal];
UILabel *lbl = selectedbutton.titleLabel;
It always fail with the error :
-[UICIColor colorSpaceName]: unrecognized selector sent to instance 0xa9864f0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UICIColor colorSpaceName]: unrecognized selector sent to instance 0xa9864f0'
*** First throw call stack: [...]
libc++abi.dylib: terminate called throwing an exception
I found a workaround :
before using my colorWithCIColor, I made a copy of it with :
newcolor = [UIColor colorWithCGColor:newcolor.CGColor];
and it solves the crash.
Strange, anyway
I was in the same situation as you where I had a string of the constituent values of the RGB UIColor
Although using CIColor colorWithString: is much more compact to get rid of the error I converted it manually:
NSArray * colorParts = [color componentsSeparatedByString: #" "];
CGFloat red = [[colorParts objectAtIndex:0] floatValue];
CGFloat green = [[colorParts objectAtIndex:1] floatValue];
CGFloat blue = [[colorParts objectAtIndex:2] floatValue];
CGFloat alpha = [[colorParts objectAtIndex:3] floatValue];
UIColor * newColor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
[button setTitleColor:newcolor forState:UIControlStateNormal];
This certainly isn't the most elegant way of doing it but is a good fix if this suddenly becomes an issue after an update.

nspasteboard readObjectsForClasses blows up

I am trying to implement a simple drag and drop operation in a tableView. However, when I try to retrieve the data from the pasteboard using readObjectsForClasses, I get a runtime exception saying
"[__NSCFConstantString initWithBytes:length:encoding:]: unrecognized selector sent to instance "
This is my acceptDrop method where the problem occurs. Can someone please help point out what I am doing wrong.
(BOOL) tableView: (NSTableView *) view
acceptDrop: (id ) info
row: (NSInteger) row
dropOperation: (NSTableViewDropOperation) op
{
NSArray *pBoardClasses = [[NSArray alloc] initWithArray:[NSArray arrayWithObjects:[NSStringPboardType class], [NSDragPboard class], nil]];
NSPasteboard *board =[info draggingPasteboard];
if(![board canReadObjectForClasses:pBoardClasses options:nil])
{
NSLog(#"No acceptable data format in pasteboard. Cannot perform this operation!");
return NO;
}
NSArray * dFromPboard = [board readObjectsForClasses:pBoardClasses options:nil];
}
use [NSString class] instead of [NSStringPboardType class]
also: [NSDragPboard class] looks like it's not right. Remove this and test if it works with [NSString class] only.
NSArray *stringObjects = [pboard readObjectsForClasses:#[ [NSString class], [NSAttributedString class] ] options:#{}];
if(stringObjects.count > 0) {
NSString *myStr = stringObjects[0];
//…
}
//…

Resources