How to implement UIUserNotificationActionBehavior.TextInput from iOS 9 in iOS 8? - swift

I registered UIMutableUserNotificationAction:
let responseTextAction = UIMutableUserNotificationAction()
responseTextAction.identifier = "text"
responseTextAction.title = "New text"
if #available(iOS 9.0, *) {
responseTextAction.behavior = UIUserNotificationActionBehavior.TextInput
} else {
// Fallback on earlier versions
}
This is screenshot from iOS 9:
and from iOS 8:
How can I implement text input for iOS 8 also.

Text Input for Notifications is only available in iOS 9+. Previous versions will default to a standard notification as you have seen.

Related

How to detect user's device has dynamic island in UIKit?

In my application, I was implemented pull-to-refresh feature and custom loading icon. In IPhone which has dynamic island, It was overlapsed my loading icon.
I want to detect device which has dynamic island or not. If it has, I will add some top space to it.
Currently, as far as I know, dynamic island is will included in ActivityKit on late of 2022. You can check from this link for ActivityKit and Apple's thread about it. And Apple doesn't provide way to check dynamic island is on device or not.
But there is a workaround for you to get the thing you want. Currently dynamic island only available on iPhone 14 Pro and iPhone 14 Pro Max. So just need to check this both device.
Update: Thanks to this link for type model, name model type of iPhone 14 Pro and iPhone 14 Pro Max is iPhone15,2 and iPhone15,3 so we just need to check these case.
Code will be like this
extension UIDevice {
func checkIfHasDynamicIsland() -> Bool {
if let simulatorModelIdentifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
let nameSimulator = simulatorModelIdentifier
return nameSimulator == "iPhone15,2" || nameSimulator == "iPhone15,3" ? true : false
}
var sysinfo = utsname()
uname(&sysinfo) // ignore return value
let name = String(bytes: Data(bytes: &sysinfo.machine, count: Int(_SYS_NAMELEN)), encoding: .ascii)!.trimmingCharacters(in: .controlCharacters)
return name == "iPhone15,2" || name == "iPhone15,3" ? true : false
}
}
Usage
let value = UIDevice().checkIfHasDynamicIsland()
print("value: ", value)
According to the live activity documentation, we can only detect whether the device supports Live activity, but we don't know if the device has dynamic island
I use the window safeAreaInsets value to detect dynamic island. when the device orientation is portrait, safeAreaInsets.top is equal to 59(Display Zoom Default),
or 51(Display Zoom Large Text).
This is likely to support the iPhone15 Pro/iPhone15 Pro Max and later models.
usage: print(UIDevice.current.hasDynamicIsland)
extension UIDevice {
// Get this value after sceneDidBecomeActive
var hasDynamicIsland: Bool {
// 1. dynamicIsland only support iPhone
guard userInterfaceIdiom == .phone else {
return false
}
// 2. Get key window, working after sceneDidBecomeActive
guard let window = (UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.flatMap { $0.windows }.first { $0.isKeyWindow}) else {
print("Do not found key window")
return false
}
// 3.It works properly when the device orientation is portrait
return window.safeAreaInsets.top >= 51
}
}

Replacement for subscriberCellularProviderDidUpdate

I want to detect when a user changes the SIM card.
I tried using subscriberCellularProviderDidUpdate but after removing and reinserting the SIM card the closure/block never gets called. Also the instance property is deprecated. Is there a replacement?
subscriberCellularProviderDidUpdateNotifier appears to have been replaced with serviceSubscriberCellularProvidersDidUpdateNotifier as of iOS 12.
If you need to support iOS 11 or earlier in addition to iOS 12 you can something like:
let ct = CTTelephonyNetworkInfo()
if #available(iOS 12.0, *) {
ct.serviceSubscriberCellularProvidersDidUpdateNotifier = { (carrier) in
// carrier is a String
}
} else {
ct.subscriberCellularProviderDidUpdateNotifier = { (carrier) in
// carrier is a CTCarrier
}
}

How to restrict api to an iOS version say just 10

I want to be able to only allow a api to be called if the version is 10.*. I know usually we use #available(10.0, *), but this means 10 and above.
How do i restrict 10 to <11?
Here is the persudo code:
if device is ios10 but less than 11 {
//Do this only for iOS10.*
}
You can use #available instead of #available, just tested this and it seems to do what you need:
if #available(iOS 11.0, *) {
// leave blank if you don't need to do anything here
} else if #available(iOS 10.0, *) {
print("You're on iOS 10!")
}
You can use code:
let os = ProcessInfo().operatingSystemVersion
switch (os.majorVersion, os.minorVersion) {
case (10, 0): // iOS 10.0
// Do your code
default:
break // Some other version
}
Or if you want to use for all 10.* versions of OS, then simply skip minor version:
let os = ProcessInfo().operatingSystemVersion
if os.majorVersion == 10 {
// Do your code
}

swift iOS 7 replacement counter for time and distance

I have the following code in my running app
func eachSecond(timer: NSTimer) {
if deviceversion.floatValue >= 8.0 {
seconds++
let secondsQuantity = HKQuantity(unit: HKUnit.secondUnit(), doubleValue: seconds)
timeLabel.text = "Tijd: " + secondsQuantity.description
let distanceQuantity = HKQuantity(unit: HKUnit.meterUnit(), doubleValue: distance)
distanceLabel.text = "Afstand: " + distanceQuantity.description
let paceUnit = HKUnit.secondUnit().unitDividedByUnit(HKUnit.meterUnit())
let paceQuantity = HKQuantity(unit: paceUnit, doubleValue: seconds / distance)
paceLabel.text = "Snelheid: " + paceQuantity.description
}else{
}
}
This is using health kit which comes with ios 8. I also want people to use the app on their 7.1 iPhone. Is there an easy way to get the same information by using swift for iOS 7?
I can't seem to find how to do this and store this for later use. They made it real easy in iOS 8.
If you need to deploy your app on iOS releases before 8.0, you should perform the calculations directly rather than relying on HealthKit to provide the conveniences.

How to check if a font is available in version of iOS?

I'm currently working on an app that uses the "ChalkboardSE-Regular" font and my deployment target is 4.0+. This font was not available in 4.1 but it is supported in 4.3. What would be the best way to go about checking if the font exists and if it does not, use another supported font on the <4.1 versions of iOS. [UIFont familyName] returns a list of these fonts "Chalkboard SE"
Thanks in advance!
T
[UIFont familyName] is supported back to iPhone OS 2.0 (before 2.0, third-party apps were not allowed on iPhone or iPod touch) , so I would use that to check to see if a font family exists on the current device, and if it doesn't exist, use a suitable fall-back font for that version of iOS. Here's John Gruber's list of iPhone fonts from the original iPhone in 2007 (contrasted with the fonts in Mac OS X of the day). I haven't checked them all, but the iOS fonts I did check are still in iOS 5:
http://daringfireball.net/misc/2007/07/iphone-osx-fonts
Here's an example of using [UIFont familyName]:
NSLog (#"Font families: %#", [UIFont familyNames]);
This will produce a list like this:
Font families: (
Thonburi,
"Snell Roundhand",
"Academy Engraved LET", ... et cetera.
Once you know the family name, you can use the UIFont class method fontNamesForFamilyName to get an NSArray of the names of all the fonts in the family. For example:
NSLog (#"Courier New family fonts: %#", [UIFont fontNamesForFamilyName:#"Courier New"]);
That will produce a list like this:
Courier New family fonts: (
CourierNewPSMT,
"CourierNewPS-BoldMT",
"CourierNewPS-BoldItalicMT",
"CourierNewPS-ItalicMT"
)
The following example code prints a list of each font in every family on the current device:
NSArray *fontFamilies = [UIFont familyNames];
for (int i = 0; i < [fontFamilies count]; i++)
{
NSString *fontFamily = [fontFamilies objectAtIndex:i];
NSArray *fontNames = [UIFont fontNamesForFamilyName:[fontFamilies objectAtIndex:i]];
NSLog (#"%#: %#", fontFamily, fontNames);
}
For more information, check out the iOS Developer Reference document for the UIFont class methods familyNames and fontNamesForFamilyName:.
If you use Swift you can print all fonts (see below). You can also check if the font exists.
for family in UIFont.familyNames {
print("\(family)")
for name in UIFont.fontNames(forFamilyName: family) {
print(" \(name)")
}
}
Swift 5.x
Forget those for-loops, this is the easiest way to see fonts supported by iOS.
Just run any of the following in a playground.
Family Names Only
Input
dump(UIFont.familyNames)
Output
▿ 75 elements
- "Copperplate"
- "Heiti SC"
- "Kohinoor Telugu"
...
Font Names Only
Input
dump(UIFont.familyNames.compactMap { UIFont.fontNames(forFamilyName: $0) })
Output
▿ 248 elements
- "Copperplate-Light"
- "Copperplate"
- "Copperplate-Bold"
...
Font Names for Specified Family Name
Input
dump(UIFont.fontNames(forFamilyName: "Helvetica Neue"))
Output
▿ 14 elements
- "HelveticaNeue-Italic"
- "HelveticaNeue-Bold"
- "HelveticaNeue-UltraLight"
- "HelveticaNeue-CondensedBlack"
...
This font was not available in 4.1 but it is supported in 4.3. What would be the best way to go about checking if the font exists
Simply ask for the font in the usual way using UIFont* f = [UIFont fontWithName:... size:...];. If the font isn't available, the result (f) will be nil.
(One way to guarantee the availability of a font is to include it in the app bundle...)
For iOS9 / Swift 2.0, none of above won't work as the syntax changed a little. I also personally prefer to use extension (I choose to create one for UIFont, as it fits the best and modified #API answer as this was the best one):
extension UIFont {
static func availableFonts() {
// Get all fonts families
for family in UIFont.familyNames() {
NSLog("\(family)")
// Show all fonts for any given family
for name in UIFont.fontNamesForFamilyName(family) {
NSLog(" \(name)")
}
}
}
}
Now you can just call:
UIFont.availableFonts()
And it will tell you all the fonts in the form of
: Bangla Sangam MN
: BanglaSangamMN-Bold
: BanglaSangamMN
: Zapfino
: Zapfino
Hope it helps!
This is what i did on objective c to find out if font is available or not
NSFont *font = [NSFont fontWithName:#"thefont" size:25.0];
if (font==nil) {
// thefont is available
} else {
// thefont is not available
}
Swift version:
UIFont.familyNames().sort( { $0 < $1 } ).forEach({ print("\($0)"); UIFont.fontNamesForFamilyName("\($0)").sort( { $0 < $1 } ).forEach({ print(" \($0)") }) })
Well, rather than writing a single line of code, you can just open http://iosfonts.com and check availability based on your iOS version support. You can also know how would it look like.
Here is a conversion of Steves answer to Swift code (for quick copy and paste purpose):
var fontFamilies = UIFont.familyNames()
for (var i:Int = 0; i < fontFamilies.count; i++) {
var fontFamily: NSString = fontFamilies[i] as NSString
var fontNames: NSArray = UIFont.fontNamesForFamilyName(fontFamilies[i] as String) as NSArray
NSLog ("%#: %#", fontFamily, fontNames)
}
Try to init with that font name, and if it's nil do something else. Swift code:
if let font = UIFont(name: name, size: size) { // ok } else { // not ok }
I figured that using swiftui preview, you can make a app that shows what each font looks like using the preview simulator.
struct TitleViewModifier_Previews:
PreviewProvider {
static var previews: some View {
List{
ForEach(UIFont.familyNames.sorted(),id: \.self){ family in
ForEach(UIFont.fontNames(forFamilyName: family), id: \.self){ eachFont in
Text(eachFont)
.font(.custom(eachFont, size: 22))
}
}
}
}
}
Here is what my preview looks like
Press the play button to make the list interactive and scrollable
For objective-c
for (NSString *family in UIFont.familyNames) {
NSLog(#"family %#",family);
for (NSString *name in [UIFont fontNamesForFamilyName:family]) {
NSLog(#" name = %#",name);
}
}
print("Font families: %#", UIFont.familyNames)
Swift 4.x
UIFont.familyNames().sort( { $0 < $1 } ).forEach({ print("\($0)"); UIFont.fontNamesForFamilyName("\($0)").sort( { $0 < $1 } ).forEach({ print(" \($0)") }) })
Swift 5.x
UIFont.familyNames.sorted( by: { $0 < $1 } ).forEach({ print("\($0)"); UIFont.fontNames(forFamilyName: "\($0)").sorted(by: { $0 < $1 } ).forEach({ print(" \($0)") }) })
To check if UIFont is registered:
let fontName = "Helvetica"
UIFont.familyNames.flatMap(UIFont.fontNames).contains(fontName)