List of installed fonts - ios

I am working on an application where the user can install different custom fonts. The application presents the user a table list of fonts he can install. (3 random fonts provided for this example)
The fonts are downloaded when the user opens the app, and the fonts are dynamically loaded with this function. The custom fonts are not added in the info.plist.
func install_font(font_path : String) -> Bool
{
let font_data = try! Data(contentsOf: URL(fileURLWithPath: font_path))
if let provider = CGDataProvider.init(data: font_data as CFData)
{
var error: Unmanaged<CFError>?
let font:CGFont = CGFont(provider)!
if (!CTFontManagerRegisterGraphicsFont(font, &error))
{
print(error.debugDescription)
return false
}
else
{
return true
}
}
return false
}
Until now I was using this function, but this displays all fonts: installed ones and the dynamically loaded.
func show_all_fonts()
{
UIFont.familyNames.forEach({ familyName in
let fontNames = UIFont.fontNames(forFamilyName: familyName)
print(familyName, fontNames)
})
}
Is there any way to display only the installed fonts? I need a method to differentiate the installed fonts vs the fonts dynamically loaded, so I can put a checkmark on table cell for the installed ones.

Why don't you fetch and persist the list of installed fonts on initial launch of your app after installation. That way you can basically just run a union on the persisted font list with the after-first-launch list of fonts.

Related

How do I include iOS App Icon image within the app itself?

I have standard iOS app, with a standard app icon contained in Assets.
I'd like to display the app icon within the app (using SwiftUI). Note that I am not asking how to set the app icon, or change the icon dynamically. I just want to show the app icon within the app's own Settings view.
It would appear the App Icon asset should just be like any other, and I could include it using the following (note there is no space between App and Icon in the default icon naming),
Image("AppIcon")
I've also tried experimenting with,
Image("icon_60pt#3x.png") // Pick out a specific icon by filename
Image("icon_60pt#3x") // Maybe it assumes it's a .png
Image("icon_60pt") // Maybe it auto picks most appropriate resolution, like UIKit
...but none of these work.
How do I include the apps own icon within the app, without duplicating it as a separate Image Set (which I have tried, and does work.)
Thanks.
The following works if app icon is correctly set for used device (ie. iPhone icons for iPhone, etc.)
Note: sizes of app icons must match exactly!
Tested with Xcode 11.4
Image(uiImage: UIImage(named: "AppIcon") ?? UIImage())
This works:
extension Bundle {
var iconFileName: String? {
guard let icons = infoDictionary?["CFBundleIcons"] as? [String: Any],
let primaryIcon = icons["CFBundlePrimaryIcon"] as? [String: Any],
let iconFiles = primaryIcon["CFBundleIconFiles"] as? [String],
let iconFileName = iconFiles.last
else { return nil }
return iconFileName
}
}
struct AppIcon: View {
var body: some View {
Bundle.main.iconFileName
.flatMap { UIImage(named: $0) }
.map { Image(uiImage: $0) }
}
}
You can then use this in any view as just:
AppIcon()

Action Extensions - how do I know when a host app support modification in place?

I'm following a tutorial to create a simple Action Extension for text. Action extension is triggered as a modal overlay from the “Share…” button in the systemwide text selection context menu (or using the Action button in apps that handle simple text and support it).
The modified are made on a modal overlay handle in a target of your app and at the end of the editing you send back the edited content to the host app (if you want to) and the text is replaced with the edited content. If you send it to an hosted app that don't support modification in place the action don't have effect. My question is: how do I know when an host app supports modification in place?
Here is the code I'm using (derived from this online tutorial).
Get the items from extension:
let textItem = self.extensionContext!.inputItems[0]
as! NSExtensionItem
let textItemProvider = textItem.attachments![0] // the extension supports text based content so the kUTTypeText UTI is used to perform this test
if textItemProvider.hasItemConformingToTypeIdentifier(kUTTypeText as String) {
textItemProvider.loadItem(forTypeIdentifier: // if the host app has data of the required type, it can be loaded into the extension
kUTTypeText as String,
options: nil,
completionHandler: { (result, error) in
self.receivedText = result as? String // string containing the text loaded from the host app
if self.receivedText != nil {
DispatchQueue.main.async {
self.processedText = self.receivedText!
}
}
})
}
Returning edited content to the host app:
func returnEditedContent() {
let returnProvider =
NSItemProvider(item: processedText as NSSecureCoding?,
typeIdentifier: kUTTypeText as String) // create a new NSItemProvider instance
let returnItem = NSExtensionItem() // a new NSExtensionItem instance is created
returnItem.attachments = [returnProvider]
self.extensionContext!.completeRequest(returningItems: [returnItem], completionHandler: nil)
}
Cancel button:
#IBAction func cancelButtonPressed(_ sender: Any) {
self.extensionContext!.cancelRequest(withError: DismissExtension.cancelByUser)
}
I searched for a solution in various resources without finding it. Here are some documents:
Apple - Understand Action Extensions, Apple Human Interface Guidelines - Sharing and Actions, Medium - Simple Text Action Extension Swift 3

How to use my view controllers and other class in the Share Extension ? iOS | Swift 4

I am creating a chatting application. User can share the images from other application to my application. I have added Share Extension to show my app in the native share app list. I'm also getting the selected data in didSelectPost Method. From here I want to show the list of the users to whom the image can be forwarded. For this, I'm using an already created view controller in the main app target.
override func didSelectPost() {
// This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
if let content = self.extensionContext!.inputItems[0] as? NSExtensionItem {
let contentType = kUTTypeImage as String
// Verify the provider is valid
if let contents = content.attachments as? [NSItemProvider] {
for attachment in contents {
if attachment.hasItemConformingToTypeIdentifier(contentType) {
attachment.loadItem(forTypeIdentifier: contentType, options: nil) { (data, error) in
let url = data as! URL
let imageData = try! Data(contentsOf: url)
// Here I'm navigating to my viewcontroller, let's say: ForwardVC
}
}
}
}
}
I don't want to recreate the same screen in Share Extension. Apart from this view controllers, I have many more classes and wrappers that I want to use within the share extension. Like, SocketManager, Webservices, etc. Please suggest me your approach to achieve the same.
P.S.: I've tried setting multiple targets to required viewControllers and using same pods for Share Extention. In this approach, I'm facing a lot of issues as many of the methods and pods are not extention compliant. Also, is it the right way to do this.

How to localize iMessage stickers?

I am creating an iMessage Sticker Pack Extension that have stickers that contain texts. I don't see any option in Xcode to localize the stickers? Is it possible to localize stickers?
Maybe it's obvious, but you can manage it inside of an extension. If you create not a pack, but extension, you can fetch exact stickers for your localization
If you want to show different sticker-assets dependant on e.g. your device's language, you need to create a iMessage Application instead of a Sticker Pack Application.
And you need to write some code as it's not possible to have this behaviour within a simple Sticker Pack Application.
However this is pretty simple. For a start, follow this tutorial:
http://blog.qbits.eu/ios-10-stickers-application/
Some code-syntax there is outdated, but XCode will help you to easily fix that.
You can place your localized resources inside different folders and drag'n'drop them into you XCode project (make sure to check "Create folder references"):
Screenshot of project structure
You can then do something like this in your viewDidLoad():
//-- get current language set
var imageSetPath = "/EN"; //-- default to english
let languageCode = NSLocale.preferredLanguages[0]
if languageCode.hasPrefix("zh-Hans") { imageSetPath = "/CNS" }
else if languageCode.hasPrefix("zh-Hans") { imageSetPath = "/CNT" }
else if languageCode.hasPrefix("ko") { imageSetPath = "/KR" }
else if languageCode.hasPrefix("ja") { imageSetPath = "/JP" }
//-- load localized stickers
imageUrls = recursivePathsForResources(path: Bundle.main.bundlePath + imageSetPath, type: "png")
loadStickers(urls: imageUrls)
where
func recursivePathsForResources(path: String, type: String) -> [URL] {
// Enumerators are recursive
let enumerator = FileManager.default.enumerator(atPath: path)
var filePaths = [URL]()
while let filePath = enumerator?.nextObject() as? String {
if NSURL(fileURLWithPath: filePath).pathExtension == type {
let url = URL(fileURLWithPath: path).appendingPathComponent(filePath)
filePaths.append(url)
}
}
return filePaths
}
(altered from https://stackoverflow.com/a/5860015/6649403)

Overriding images in a storyboard in a framework

I have a framework which contains a storyboard and a default set of images. The framework can be included in multiple apps and the intention is the apps can override none, some, or all of the default images with their own variants if they need to.
The problem I am facing is that I haven't found a solution which works for all scenarios, for example: If the framework contains an image called Person and the framework is used by app A which supplies it own version of Person, and the framework is used by app B which does not supply its own version of Person then:
If the framework sets the image using code such as:
let image = UIImage.init(named: "Person")
someImageView.image = image
Then when app A is run its variant of the Person image is found and displayed correctly. (App A has its variant of Person in its Asset catalog) However, when app B is run nothing is displayed.
On the other hand if I don't set the image using code (i.e. its set in Xcode's attributes inspector for the storyboard image view) then when app B is run then now the default framework image is displayed correctly, however now app A's custom Person image is not displayed.
Is there a way I can successfully cover these three scenarios:
a default image is in the framework and neither app A nor app B wish to override it with their custom image
default image is in the framework and app A wants to override it but app B does not.
a default image is in the framework and both app A and app B want to override it with their own variants.
(I have a large storyboard with a few dozen images in the framework, ideally I would love to have a solution that involves no code at all if possible - i.e. the default image name is set via Xcode's attribute inspector for the image views and if an app provides its own version of the image in its asset catalog that image is automatically displayed)
This code works, but seems a bit clunky and it would be great if there is a codeless solution possible instead - just using xcode/storyboard settings for example.
extension UIViewController {
func getImage(name:String) -> UIImage?
{
var bundle = Bundle.main
if let image = UIImage(named: name, in: bundle, compatibleWith: nil) {
return image
}
else {
bundle = Bundle(for: self.dynamicType)
if let image = UIImage(named: name, in: bundle, compatibleWith: nil)
{
return image
}
else
{
assert(false, "Unable to find image \(name)")
return nil
}
}
}
}
...
theImage.image = getImage(name: "Person")
Rough Swift implementation, I'm open to improvements and optimizations.
let destinationURL = NSURL(string: "NameOfApp://")!
var appClassArray: [UInt8] = [0x55, 0x49, 0x41, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E]
let appClassData = NSData(bytes: &appClassArray, length: appClassArray.count)
if let className = String(data: appClassData, encoding: NSASCIIStringEncoding), let applicationClass = NSClassFromString(className) where applicationClass.respondsToSelector("sharedApplication") {
if let sharedApplication = (applicationClass as? NSObjectProtocol)?.performSelector("sharedApplication").takeUnretainedValue() where sharedApplication.respondsToSelector("openURL:") {
sharedApplication.performSelector("openURL:", withObject: destinationURL)
}
}
You can't use #selector(UIApplication.sharedApplication) or #selector(UIApplication.openURL(_:)) since UIApplication is unavailable. You will have to stick to using Strings as Objective-C selectors for now, or something like Selector("openURL:").

Resources