I want to copy image and text (both) to UIPasteBoard.
Is it possible to copy both the text and image.
Here I can copy image only or text only .
How to copy both ?
My code for copy image is as follows,
UIPasteboard *pasteBoard = [UIPasteboard pasteboardWithName:UIPasteboardNameGeneral create:NO];
pasteBoard.persistent = YES;
NSData *data = UIImagePNGRepresentation(newImage);
[pasteBoard setData:data forPasteboardType:(NSString *)kUTTypePNG];
Thanks in advance !!!!!
Here is my code and it is working perfectly on my device.
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
pasteboard.persistent = NO;
NSMutableDictionary *text = [NSMutableDictionary dictionaryWithCapacity:1];
[text setValue:captionLabel.text forKey:(NSString *)kUTTypeUTF8PlainText];
NSMutableDictionary *image = [NSMutableDictionary dictionaryWithCapacity:1];
[image setValue:gratitudeImageView.image forKey:(NSString *)kUTTypePNG];
pasteboard.items = [NSArray arrayWithObjects:image,text, nil];
You should be setting the items property of the pasteboard-
The description of items from the reference is-
items
The pasteboard items on the pasteboard. #property(nonatomic,copy)
NSArray *items Discussion
The value of the property is an array of dictionaries. Each dictionary
represents a pasteboard item, with the key being the representation
type and the value the data object or property-list object associated
with that type. Setting this property replaces all of the current
pasteboard items.
So, you can add two dictionaries to an array, with key value pairs being & and set this array to the items property.
In my experience, the official way simply does not work in iOS. Instead of creating an individual dictionary for each item and adding those to the array (as stated in the documentation), add all items to a single dictionary, then make an array with that single dictionary and set that to the pasteboard.
Like this:
NSMutableDictionary * pasteboardDict = [NSMutableDictionary dictionary];
[pasteboardDict setObject:someData forKey:someUTIkey];
[pasteboardDict setObject:someOtherData forKey:someOtherUTIkey];
[[UIPasteboard generalPasteboard]setItems:[NSArray arrayWithObject:pasteboardDict]];
This question was asked a long time ago, but it's still relevant - and especially since Apple docs don't make Swift multi-format UIPasteboard APIs very clear. Having struggled to figure out how to do multiple-format copy & paste, I thought I'd share my solution in case it helped anyone else. In my case, I needed to support an internal format (containing all the particulars), as well as image and text versions for pasting into other apps.
First, you need to get access to the UTI constants - you'll get unresolved symbols without adding this at the top of your file:
import MobileCoreServices
Then define your format UTI:
let my_private_uti = "com.mydomain.myapp.myformat"
Here's the code for an example multi-format copy (in my case for a music program):
externalRepresentation = "[A7]"
internalRepresentation = "A7:0 0 2 0 2 0"
image = UIImage()
// fill image with chord diagram...
let pasteboard =
[ [kUTTypeUTF8PlainText as String : externalRepresentation],
[kUTTypePNG as String: UIImagePNGRepresentation(image!)!],
[my_private_uti: internalRepresentation]]
UIPasteboard.general.setItems(pasteboard)
And now for the the paste. I want to accept my internal format if it's available, and fall back to processing text if it's not. (Don't do anything with a graphical format in my case.)
//Handle internal format
if let pastedata = UIPasteboard.general.data(forPasteboardType:my_private_uti, inItemSet:nil) {
if pastedata.count > 0 {
if let ourformat = String(data: pastedata[0] as! Data, encoding: .utf8) {
// Process ourformat string
print("Pasted internal representation: \(ourformat)")
return
}
}
}
// Handle plain text format
if let pastedata = UIPasteboard.general.data(forPasteboardType:kUTTypeUTF8PlainText as String, inItemSet:nil) {
if pastedata.count > 0 {
if let textformat = String(data: pastedata[0] as! Data, encoding: .utf8) {
// Process normal text
print("Pasted external representation: \(textformat)")
return
}
}
}
Seems like it's pointless to set the persistent boolean in iOS, from Apple:
iOS, public (system) pasteboards are persistent, but by default private (application) pasteboards are not. These private pasteboards do not continue to exist when the application that creates them quits. However, you can mark application pasteboards as persistent.
Related
I am implementing a Cache in my iOS app, that would keep images downloaded in RAM.
I did some research and found some code but most of them were for caching images to permanent storage.
I tried NSCache but couldn't work it around for my need.
The requirements are:
Limit on saving images. e.g. 100.
As the Cache limit is reached, it should remove most older image inserted before adding a new one.
I'm not sure about the exact word but I think it should be called FIFO cache (First in first out).
After some research, I did the following implementation.
static NSMutableDictionary *thumbnailImagesCache = nil;
+ (UIImage *)imageWithURL:(NSString *)_imageURL
{
if (thumbnailImagesCache == nil) {
thumbnailImagesCache = [NSMutableDictionary dictionary];
}
UIImage *image = nil;
if ((image = [thumbnailImagesCache objectForKey:_imageURL])) {
DLog(#"image found in Cache")
return image;
}
/* the image was not found in cache - object sending request for image is responsible to download image and save it to cache */
DLog(#"image not found in cache")
return nil;
}
+ (void)saveImageForURL:(UIImage *)_image URLString:(NSString *)_urlString
{
if (thumbnailImagesCache == nil) {
thumbnailImagesCache = [NSMutableDictionary dictionary];
}
if (_image && _urlString) {
DLog(#"adding image to cache")
if (thumbnailImagesCache.count > 100) {
NSArray *keys = [thumbnailImagesCache allKeys];
NSString *key0 = [keys objectAtIndex:0];
[thumbnailImagesCache removeObjectForKey:key0];
}
[thumbnailImagesCache setObject:_image forKey:_urlString];
DLog(#"images count in cache = %d", thumbnailImagesCache.count)
}
}
Now the problem is that I'm not sure weather this is the correct/efficient solution. Any one have any better idea/solution?
Your assumption about the order of the keys is certainly incorrect. The order of the keys in an NSDictionary is unspecified, the key and value at index 0 need not be the oldest one. You shall store the creation date of each image in the method where you put them in the cache dictionary.
Apart from that, the rest of the code seems valid.
How to compare 2 arrays with different value
Array one has words en the other has images
NSDictionary *dict = [NSDictionary dictionaryWithObjects:array2 forKeys:array1];
I think you need to use an NSDictionary. This is how you do that (using the new Objective C literal syntax)
NSDictionary *dictionary = #{
#"dog" : #"dog.jpg",
#"apple" : #"apple.jpeg",
#"clown" : #"clown.gif"
};
To retrieve the image filename for "dog" from this dictionary do this:
NSString *fileName = dictionary[#"dog"];
When a button is clicked you can simply take that value and search into images array to get the matching image name for e-g,
NSString *selValue = #"dog";
for (NSString *obj in imagesArray) {
if ([obj rangeOfString:selValue].location != NSNotFound) {
NSString *imageName = obj;
break;
}
}
This is not the fully working code with your requirement, can be used as an Idea as you have images.
Assuming your word and image are in same index
I have just implemented similar kind of situation with strings named A.jpg, The idea is kept same, You need to transform accordingly.
NSMutableArray *words=[[NSMutableArray alloc]initWithObjects:#"A",#"B",#"C",#"D", nil];
NSMutableArray *images=[[NSMutableArray alloc]initWithObjects:#"A.jpg",#"B.jpg",#"C.jpg",#"D.jpg", nil];
id selectedWord=#"C";//This is storing which word you have selected
id selectedImage=[images objectAtIndex:[words indexOfObject:selectedWord]];//this will store the image
NSLog(#"%#",selectedImage);//now you can display the image in imageview
If words and images are not in anyorder
//words array is of no use, you can simply find which word you selected by extracting before "." , but as I am not aware of exact requirement I have left words array.
NSMutableArray *words=[[NSMutableArray alloc]initWithObjects:#"A",#"B",#"C",#"D", nil];
NSMutableArray *images=[[NSMutableArray alloc]initWithObjects:#"B.jpg",#"D.jpg",#"C.jpg",#"A.jpg", nil];
id selectedWord=#"B";
NSInteger indexOfSelectedWord;
for (NSString *imageName in images) {
if ([[[imageName componentsSeparatedByString:#"."]objectAtIndex:0]isEqualToString:selectedWord]) {
indexOfSelectedWord=[images indexOfObject:imageName];
}
}
id selectedImage=[images objectAtIndex:indexOfSelectedWord];
NSLog(#"%# & %#",selectedWord ,selectedImage);
How can I access the Bundle Seed ID/Team ID/App Identifier Prefix string programmatically? (These are all the same thing as far as I can tell).
I am using the UICKeychainStore keychain wrapper to persist data across several applications. Each of these applications has a shared keychain access group in their entitlement plists, and share the same provisioning profile. By default, the keychain services use the first access group in the plist as the access group to save data to. This looks like "AS234SDG.com.myCompany.SpecificApp" when I debug UICKeychainStore. I would like to set the access group to "AS234SDG.com.myCompany.SharedStuff", but I can't seem to locate how to get the "AS234SDG" string of the access group programmatically, and would like to avoid hard-coding it if possible.
Info.plist can have your own information and if you write a value with $(AppIdentifierPrefix), it is replaced to the real app identifier prefix at building phase.
So, try this:
In your Info.plist, add an info about app identifier prefix.
<key>AppIdentifierPrefix</key>
<string>$(AppIdentifierPrefix)</string>
You can then retrieve it programmatically with Objective-C:
NSString *appIdentifierPrefix =
[[NSBundle mainBundle] objectForInfoDictionaryKey:#"AppIdentifierPrefix"];
and with Swift:
let appIdentifierPrefix =
Bundle.main.infoDictionary!["AppIdentifierPrefix"] as! String
Note that appIdentifierPrefix ends with a period; e.g. AS234SDG.
You can programmatically retrieve the Bundle Seed ID by looking at the access group attribute (i.e. kSecAttrAccessGroup) of an existing KeyChain item. In the code below, I look up for an existing KeyChain entry and create one if it doesn't not exist. Once I have a KeyChain entry, I extract the access group information from it and return the access group's first component separated by "." (period) as the Bundle Seed ID.
+ (NSString *)bundleSeedID {
NSString *tempAccountName = #"bundleSeedID";
NSDictionary *query = #{
(__bridge NSString *)kSecClass : (__bridge NSString *)kSecClassGenericPassword,
(__bridge NSString *)kSecAttrAccount : tempAccountName,
(__bridge NSString *)kSecAttrService : #"",
(__bridge NSString *)kSecReturnAttributes: (__bridge NSNumber *)kCFBooleanTrue,
};
CFDictionaryRef result = nil;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
if (status == errSecItemNotFound)
status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
if (status != errSecSuccess) {
return nil;
}
status = SecItemDelete((__bridge CFDictionaryRef)query); // remove temp item
NSDictionary *dict = (__bridge_transfer NSDictionary *)result;
NSString *accessGroup = dict[(__bridge NSString *)kSecAttrAccessGroup];
NSArray *components = [accessGroup componentsSeparatedByString:#"."];
NSString *bundleSeedID = [[components objectEnumerator] nextObject];
return bundleSeedID;
}
Here is the Swift version of #David H answer:
static func bundleSeedID() -> String? {
let queryLoad: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "bundleSeedID" as AnyObject,
kSecAttrService as String: "" as AnyObject,
kSecReturnAttributes as String: kCFBooleanTrue
]
var result : AnyObject?
var status = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(queryLoad as CFDictionary, UnsafeMutablePointer($0))
}
if status == errSecItemNotFound {
status = withUnsafeMutablePointer(to: &result) {
SecItemAdd(queryLoad as CFDictionary, UnsafeMutablePointer($0))
}
}
if status == noErr {
if let resultDict = result as? [String: Any], let accessGroup = resultDict[kSecAttrAccessGroup as String] as? String {
let components = accessGroup.components(separatedBy: ".")
return components.first
}else {
return nil
}
} else {
print("Error getting bundleSeedID to Keychain")
return nil
}
}
This is a good question but to achieve what you were intended to do, there could have been a solution
that does not require to retrieve the Bundle Seed ID.
From this article, about the same keychain wrapper you're using:
By default it will pick the first access-group specified in your
Entitlements.plist when writing and will search across all
access-groups when none is specified.
The key will then be search in all groups where access is granted.
So to solve your problem, you could add access group of all your bundle apps into your entitlements.plist instead of using a "shared stuff" group, put $(CFBundleIdentifier) as your first keychain group (your keychain wrapper will then write in this group) and you're all set
If you search in Xcode on your team's ID then you will see that this value is hosted in the build settings under the key DEVELOPMENT_TEAM.
You can retrieve this key by putting in your Info.plist file:
<key>DEVELOPMENT_TEAM</key>
<string>$(DEVELOPMENT_TEAM)</string>
Make sure to put this in every target's Info.plist file where you want to retrieve it using this code:
let teamID = Bundle.main.infoDictionary!["DEVELOPMENT_TEAM"] as! String
This solution will give you the team ID without the dot suffix.
The solution in https://stackoverflow.com/a/28714850/2743633 worked for me only to get the team ID from the main app target. It would not retrieve the team ID when doing the same for a Share Extension target.
How can I access the Bundle Seed ID/Team ID/App Identifier Prefix string programmatically? (These are all the same thing as far as I can tell).
I am using the UICKeychainStore keychain wrapper to persist data across several applications. Each of these applications has a shared keychain access group in their entitlement plists, and share the same provisioning profile. By default, the keychain services use the first access group in the plist as the access group to save data to. This looks like "AS234SDG.com.myCompany.SpecificApp" when I debug UICKeychainStore. I would like to set the access group to "AS234SDG.com.myCompany.SharedStuff", but I can't seem to locate how to get the "AS234SDG" string of the access group programmatically, and would like to avoid hard-coding it if possible.
Info.plist can have your own information and if you write a value with $(AppIdentifierPrefix), it is replaced to the real app identifier prefix at building phase.
So, try this:
In your Info.plist, add an info about app identifier prefix.
<key>AppIdentifierPrefix</key>
<string>$(AppIdentifierPrefix)</string>
You can then retrieve it programmatically with Objective-C:
NSString *appIdentifierPrefix =
[[NSBundle mainBundle] objectForInfoDictionaryKey:#"AppIdentifierPrefix"];
and with Swift:
let appIdentifierPrefix =
Bundle.main.infoDictionary!["AppIdentifierPrefix"] as! String
Note that appIdentifierPrefix ends with a period; e.g. AS234SDG.
You can programmatically retrieve the Bundle Seed ID by looking at the access group attribute (i.e. kSecAttrAccessGroup) of an existing KeyChain item. In the code below, I look up for an existing KeyChain entry and create one if it doesn't not exist. Once I have a KeyChain entry, I extract the access group information from it and return the access group's first component separated by "." (period) as the Bundle Seed ID.
+ (NSString *)bundleSeedID {
NSString *tempAccountName = #"bundleSeedID";
NSDictionary *query = #{
(__bridge NSString *)kSecClass : (__bridge NSString *)kSecClassGenericPassword,
(__bridge NSString *)kSecAttrAccount : tempAccountName,
(__bridge NSString *)kSecAttrService : #"",
(__bridge NSString *)kSecReturnAttributes: (__bridge NSNumber *)kCFBooleanTrue,
};
CFDictionaryRef result = nil;
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
if (status == errSecItemNotFound)
status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
if (status != errSecSuccess) {
return nil;
}
status = SecItemDelete((__bridge CFDictionaryRef)query); // remove temp item
NSDictionary *dict = (__bridge_transfer NSDictionary *)result;
NSString *accessGroup = dict[(__bridge NSString *)kSecAttrAccessGroup];
NSArray *components = [accessGroup componentsSeparatedByString:#"."];
NSString *bundleSeedID = [[components objectEnumerator] nextObject];
return bundleSeedID;
}
Here is the Swift version of #David H answer:
static func bundleSeedID() -> String? {
let queryLoad: [String: AnyObject] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "bundleSeedID" as AnyObject,
kSecAttrService as String: "" as AnyObject,
kSecReturnAttributes as String: kCFBooleanTrue
]
var result : AnyObject?
var status = withUnsafeMutablePointer(to: &result) {
SecItemCopyMatching(queryLoad as CFDictionary, UnsafeMutablePointer($0))
}
if status == errSecItemNotFound {
status = withUnsafeMutablePointer(to: &result) {
SecItemAdd(queryLoad as CFDictionary, UnsafeMutablePointer($0))
}
}
if status == noErr {
if let resultDict = result as? [String: Any], let accessGroup = resultDict[kSecAttrAccessGroup as String] as? String {
let components = accessGroup.components(separatedBy: ".")
return components.first
}else {
return nil
}
} else {
print("Error getting bundleSeedID to Keychain")
return nil
}
}
This is a good question but to achieve what you were intended to do, there could have been a solution
that does not require to retrieve the Bundle Seed ID.
From this article, about the same keychain wrapper you're using:
By default it will pick the first access-group specified in your
Entitlements.plist when writing and will search across all
access-groups when none is specified.
The key will then be search in all groups where access is granted.
So to solve your problem, you could add access group of all your bundle apps into your entitlements.plist instead of using a "shared stuff" group, put $(CFBundleIdentifier) as your first keychain group (your keychain wrapper will then write in this group) and you're all set
If you search in Xcode on your team's ID then you will see that this value is hosted in the build settings under the key DEVELOPMENT_TEAM.
You can retrieve this key by putting in your Info.plist file:
<key>DEVELOPMENT_TEAM</key>
<string>$(DEVELOPMENT_TEAM)</string>
Make sure to put this in every target's Info.plist file where you want to retrieve it using this code:
let teamID = Bundle.main.infoDictionary!["DEVELOPMENT_TEAM"] as! String
This solution will give you the team ID without the dot suffix.
The solution in https://stackoverflow.com/a/28714850/2743633 worked for me only to get the team ID from the main app target. It would not retrieve the team ID when doing the same for a Share Extension target.
I read the image from the photo library and I get the metadata using the assets library. I then try to read the user comment exif tag and display it in my text view. Code is here:
[assetLibrary assetForURL:assestURL resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *representation = [asset defaultRepresentation];
NSMutableDictionary *metadataDictPhoto = (NSMutableDictionary*)[representation metadata];
NSLog(#"This is the read metadata I believe: %#",[metadataDictPhoto description]);
metadataDictPhoto = metadataGlobal;
} failureBlock:^(NSError *error) {
NSLog(#"%#",[error description]);
}];
NSMutableDictionary *exifDictionary = (NSMutableDictionary*)[metadataGlobal objectForKey:(NSString*) kCGImagePropertyExifDictionary];
NSString *comment = (NSString*)[exifDictionary valueForKey:(NSString*)kCGImagePropertyExifUserComment];
textView.text = comment;
When I run it, there is no crashes but nothing is displayed in the textview. I have verified using NSLogs that the metadata received from my code is correct, as in I can see my custom exif user comment tag. If I place my mouse over comment it gives me the error . I can't get rid of this.
How can I read the string from the metadata dictionary and get it to display in the textView?
EDIT: DeePak Noticed that I mixed up an assignment statement and I changed it, but that did not fix the issue. While I was looking into this though I found that my NSLogs show that the code is that reads the metadata is passed over and then it runs the dictionary and string code which at this point the metadata isn't create. It then completes the imagepicker delegate function and then it eventually goes to the complete block and then runs the code and ouputs the dictionary and then I can see that everything is correct.
How could I get the code to run immediantly or have the program wait ntil the assetforurl completes?
You have this assignment flipped.
metadataDictPhoto = metadataGlobal;
You need to change this to
metadataGlobal = metadataDictPhoto;
metadataGlobal is probably an instance variable which is why it is not crashing as it continues to be nil here –
NSMutableDictionary *exifDictionary = (NSMutableDictionary*)[metadataGlobal objectForKey:(NSString*) kCGImagePropertyExifDictionary];
I fixed it by adding in the code outside of the completion block to inside the completion block and made sure all the variables were saved to be used then. This worked perfectly.
Now only if I could figure out why it is not writing IPTC correctly I would be set.