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
}
}
Related
I want get the language code of the device (en, es...) in my app written with Swift. How can get this?
I'm trying this:
var preferredLanguages : NSLocale!
let pre = preferredLanguages.displayNameForKey(NSLocaleIdentifier, value: preferredLanguages)
But this returns nil.
In Swift 3
let langStr = Locale.current.languageCode
It's important to make the difference between the App language and the device locale language (The code below is in Swift 3)
Will return the Device language:
let locale = NSLocale.current.languageCode
Will return the App language:
let pre = Locale.preferredLanguages[0]
Swift 4 & 5:
Locale.current.languageCode
Swift 3 & 4 & 4.2 & 5
Locale.current.languageCode does not compile regularly. Because you did not implemented localization for your project.
You have two possible solutions
1) String(Locale.preferredLanguages[0].prefix(2))
It returns phone lang properly.
If you want to get the type en-En, you can use Locale.preferredLanguages[0]
2)
Select Project(MyApp)->Project (not Target)-> press + button into Localizations, then add language which you want.
In Swift 3:
NSLocale.current.languageCode
TL;DR:
Use Bundle.main.preferredLocalizations[0] to get the language your app's UI is currently displayed in. Don't use Locale.current because it describes the region format (time, currency, distance, etc) and has nothing to do with language.
Detailed Answer:
The definite answer about how to get the language(!) code for the language your app's UI is displayed in comes from Apple engineer Quinn "The Eskimo", and I quote/paraphrase for Swift:
Locale.current returns the current locale, that is, the value set by Settings > General > Language & Region > Region Formats. It has nothing to do with the language that your app is running in. It's perfectly reasonable, and in fact quite common, for users in the field to have their locale and language set to 'conflicting' values. For example, a native English speaker living in France would have the language set to English but might choose to set the locale to French (so they get metric weights and measures, 24 time, and so on).
The language that your app runs in is determined by the language setting, that is, Settings > General > Language & Region > Preferred Language Order. When the system runs your app it takes this list of languages (the preferred list) and matches it against the list of languages that your app is localised into (the app list). The first language in the preferred list that exists in the app list is the language chosen for the app. This is what you'll find in the first entry of the main bundle's preferredLocalizations array.
Language Name from Code
To get the human-readable name of a language from its code, you can use this:
let langCode = Bundle.main.preferredLocalizations[0]
let usLocale = Locale(identifier: "en-US")
var langName = ""
if let languageName = usLocale.localizedString(forLanguageCode: langCode) {
langName = languageName
}
This will give you the English name of the current UI language.
To get current language used in your app (different than preferred languages)
NSLocale.currentLocale().objectForKey(NSLocaleLanguageCode)!
swift 3
let preferredLanguage = Locale.preferredLanguages[0] as String
print (preferredLanguage) //en-US
let arr = preferredLanguage.components(separatedBy: "-")
let deviceLanguage = arr.first
print (deviceLanguage) //en
Locale.current.languageCode returns me wrong code, so I use these extensions:
extension Locale {
static var preferredLanguageCode: String {
guard let preferredLanguage = preferredLanguages.first,
let code = Locale(identifier: preferredLanguage).languageCode else {
return "en"
}
return code
}
static var preferredLanguageCodes: [String] {
return Locale.preferredLanguages.compactMap({Locale(identifier: $0).languageCode})
}
}
Swift 5.4:
let languagePrefix = Locale.preferredLanguages[0]
print(languagePrefix)
you may use the below code it works fine with swift 3
var preferredLanguage : String = Bundle.main.preferredLocalizations.first!
I want to track the language chosen by the user in Settings app every time the user launches my app - that is not yet localized (my app is in English only). I adopted this logic:
create an enum to to make it easier to handle the languages in array
enum Language: String {
case none = ""
case en = "English"
case fr = "French"
case it = "Italian"
} // add as many languages you want
create a couple of extension to Locale
extension Locale {
static var enLocale: Locale {
return Locale(identifier: "en-EN")
} // to use in **currentLanguage** to get the localizedString in English
static var currentLanguage: Language? {
guard let code = preferredLanguages.first?.components(separatedBy: "-").last else {
print("could not detect language code")
return nil
}
guard let rawValue = enLocale.localizedString(forLanguageCode: code) else {
print("could not localize language code")
return nil
}
guard let language = Language(rawValue: rawValue) else {
print("could not init language from raw value")
return nil
}
print("language: \(code)-\(rawValue)")
return language
}
}
When you need, you can simply use the extension
if let currentLanguage = Locale.currentLanguage {
print(currentLanguage.rawValue)
// Your code here.
}
In Swift, You can get the locale using.
let locale = Locale.current.identifier
This is what I use in Swift 5 Xcode 11:
Inside the class variables:
let languagePrefix = Bundle.main.preferredLocalizations.first?.prefix(2)
This comes as a string. It returns 2 characters, i.e. "en", "es", "de"...
From this I can easily determine what language to display:
if languagePrefix == "es" { self.flipCard.setTitle("última carta", for: .normal) }
if languagePrefix == "en" { self.flipCard.setTitle("Last Card", for: .normal) }
If you want the full information of the language, then remove ?.prefex(2)
in most cases you want to get the language code of the current app UI, to send over an API to get localized response
extension Bundle {
var currentLocalizedUILanguageCode: String {
guard let code = Bundle.main.preferredLocalizations.first?.components(separatedBy: "-").first else {
return Locale.current.languageCode ?? "en"
}
return code
}
}
use like
headers["X-Language"] = Bundle.main.currentLocalizedUILanguageCode
use this function for get your system's current language code from iOS devices
func getSystemLanguageCode() -> String {
UserDefaults.standard.removeObject(forKey: "AppleLanguages")
let pref_Language = NSLocale.preferredLanguages[0] as String //"fr-IN"
let language = pref_Language.components(separatedBy: "-") //["fr","IN"]
let lang_Code = language.first?.lowercased() ?? "" //"fr"
UserDefaults.standard.set([lang_Code], forKey: "AppleLanguages")
return lang_Code
}
Almost none of the answers are correct. This is working Swift 5.7 solution.
extension Locale {
// Gets the language of the device, had to remove the content of AppleLanguages since `preferredLanguages`
// is combining the result from multiple APIs. AppleLanguage is then being set to the old value
static var preferredLanguageCode: String {
let appleLanguages = UserDefaults.standard.stringArray(forKey: kLanguage)
UserDefaults.standard.removeObject(forKey: kLanguage)
guard let preferredLanguage = preferredLanguages.first,
let code = Locale(identifier: preferredLanguage).languageCode else {
UserDefaults.standard.set(appleLanguages, forKey: kLanguage)
return "en"
}
UserDefaults.standard.set(appleLanguages, forKey: kLanguage)
return code
}
}
i'm using the library "PhoneNumberKit"
to format a UITextfield i'm using on my app that i use it to input phone number.
i am using PartialFormatter to format the input into a phone number mask style.
#IBAction func onPhoneNumberChanged(_ sender: Any) {
if (phoneNumberTextField.text?.count ?? 0 < 15) {
phoneNumberTextField.text = PartialFormatter().formatPartial(phoneNumberTextField.text ?? "")
} else {
phoneNumberTextField.deleteBackward()
}
}
my problem is that it always format the string to a LOCAL phone number, i want to force it to format it into a US phone number mask.
the library say:
The default region code is automatically computed but can be overridden if needed.
but doesn't give any examples on force region code to be to a specific country
here is the library i'm using:
https://github.com/marmelroy/PhoneNumberKit
does someone here has any exmaples on how to do it ?
thanks
fixed it by doing:
phoneNumberTextField.text = PartialFormatter(phoneNumberKit: PhoneNumberKit(), defaultRegion: "US", withPrefix: true).formatPartial(phoneNumberTextField.text ?? "")
The default region code is automatically computed but can be overridden if needed like given below
class MyTextField: PhoneNumberTextField {
override var defaultRegion: String {
get {
return "US"
}
set {} // exists for backward compatibility
}
}
Reference
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
}
}
Does anyone know of a way to get the screen name, or model name/number from a display that is connected to the system? I've been looking around for quite some time to see if there is a way to do this. The only method I've seen anyone post only works with a deprecated API (CGDisplayIOServicePort), (and there's not replacement listed for that API), so that isn't really an option.
Basically, I am wanting to give the user a list of connected screens to display the output of the app, and I feel like giving them a list of names of the displays would be much more elegant and nicer than whatever the ID is that is returned from NSScreen or CGGetActiveDisplayList, etc. It has to possible, when you go to the display preferences in OS X it gives you the names of the displays there. Anyone have any ideas?
macOS 10.15 Catalina introduced a new property localizedName for getting the external display name:
NSScreen.screens.forEach {
print($0.localizedName)
}
You can get the names of connected screens directly from IOReg
func screenNames() -> [String] {
var names = [String]()
var object : io_object_t
var serialPortIterator = io_iterator_t()
let matching = IOServiceMatching("IODisplayConnect")
let kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault,
matching,
&serialPortIterator)
if KERN_SUCCESS == kernResult && serialPortIterator != 0 {
repeat {
object = IOIteratorNext(serialPortIterator)
let info = IODisplayCreateInfoDictionary(object, UInt32(kIODisplayOnlyPreferredName)).takeRetainedValue() as NSDictionary as! [String:AnyObject]
if let productName = info["DisplayProductName"] as? [String:String],
let firstKey = Array(productName.keys).first {
names.append(productName[firstKey]!)
}
} while object != 0
}
IOObjectRelease(serialPortIterator)
return names
}
let names = screenNames()
I'm trying to have a handler in my Mac OS X app written in Swift for a global (system-wide) hotkey combo but I just cannot find proper documentation for it. I've read that I'd have to mess around in some legacy Carbon API for it, is there no better way? Can you show me some proof of concept Swift code? Thanks in advance!
Since Swift 2.0, you can now pass a function pointer to C APIs.
var gMyHotKeyID = EventHotKeyID()
gMyHotKeyID.signature = OSType("swat".fourCharCodeValue)
gMyHotKeyID.id = UInt32(keyCode)
var eventType = EventTypeSpec()
eventType.eventClass = OSType(kEventClassKeyboard)
eventType.eventKind = OSType(kEventHotKeyPressed)
// Install handler.
InstallEventHandler(GetApplicationEventTarget(), {(nextHanlder, theEvent, userData) -> OSStatus in
var hkCom = EventHotKeyID()
GetEventParameter(theEvent, EventParamName(kEventParamDirectObject), EventParamType(typeEventHotKeyID), nil, sizeof(EventHotKeyID), nil, &hkCom)
// Check that hkCom in indeed your hotkey ID and handle it.
}, 1, &eventType, nil, nil)
// Register hotkey.
let status = RegisterEventHotKey(UInt32(keyCode), UInt32(modifierKeys), gMyHotKeyID, GetApplicationEventTarget(), 0, &hotKeyRef)
I don't believe you can do this in 100% Swift today. You'll need to call InstallEventHandler() or CGEventTapCreate(), and both of those require a CFunctionPointer, which can't be created in Swift. Your best plan is to use established ObjC solutions such as DDHotKey and bridge to Swift.
You can try using NSEvent.addGlobalMonitorForEventsMatchingMask(handler:), but that only makes copies of events. You can't consume them. That means the hotkey will also be passed along to the currently active app, which can cause problems. Here's an example, but I recommend the ObjC approach; it's almost certainly going to work better.
let keycode = UInt16(kVK_ANSI_X)
let keymask: NSEventModifierFlags = .CommandKeyMask | .AlternateKeyMask | .ControlKeyMask
func handler(event: NSEvent!) {
if event.keyCode == self.keycode &&
event.modifierFlags & self.keymask == self.keymask {
println("PRESSED")
}
}
// ... to set it up ...
let options = NSDictionary(object: kCFBooleanTrue, forKey: kAXTrustedCheckOptionPrompt.takeUnretainedValue() as NSString) as CFDictionaryRef
let trusted = AXIsProcessTrustedWithOptions(options)
if (trusted) {
NSEvent.addGlobalMonitorForEventsMatchingMask(.KeyDownMask, handler: self.handler)
}
This also requires that accessibility services be approved for this app. It also doesn't capture events that are sent to your own application, so you have to either capture them with your responder chain, our use addLocalMointorForEventsMatchingMask(handler:) to add a local handler.
The following code works for me for Swift 5.0.1. This solution is the combination of the solution from the accepted answer by Charlie Monroe and the recommendation by Rob Napier to use DDHotKey.
DDHotKey seems to work out of the box but it had one limitation that I had to change: the eventKind is hardcoded to kEventHotKeyReleased while I needed both kEventHotKeyPressed and kEventHotKeyReleased event types.
eventSpec.eventKind = kEventHotKeyReleased;
If you want to handle both Pressed and Released events, just add a second InstallEventHandler call which registers the other event kind.
This the complete example of the code that registers the "Command + R" key for the kEventHotKeyReleased type.
import Carbon
extension String {
/// This converts string to UInt as a fourCharCode
public var fourCharCodeValue: Int {
var result: Int = 0
if let data = self.data(using: String.Encoding.macOSRoman) {
data.withUnsafeBytes({ (rawBytes) in
let bytes = rawBytes.bindMemory(to: UInt8.self)
for i in 0 ..< data.count {
result = result << 8 + Int(bytes[i])
}
})
}
return result
}
}
class HotkeySolution {
static
func getCarbonFlagsFromCocoaFlags(cocoaFlags: NSEvent.ModifierFlags) -> UInt32 {
let flags = cocoaFlags.rawValue
var newFlags: Int = 0
if ((flags & NSEvent.ModifierFlags.control.rawValue) > 0) {
newFlags |= controlKey
}
if ((flags & NSEvent.ModifierFlags.command.rawValue) > 0) {
newFlags |= cmdKey
}
if ((flags & NSEvent.ModifierFlags.shift.rawValue) > 0) {
newFlags |= shiftKey;
}
if ((flags & NSEvent.ModifierFlags.option.rawValue) > 0) {
newFlags |= optionKey
}
if ((flags & NSEvent.ModifierFlags.capsLock.rawValue) > 0) {
newFlags |= alphaLock
}
return UInt32(newFlags);
}
static func register() {
var hotKeyRef: EventHotKeyRef?
let modifierFlags: UInt32 =
getCarbonFlagsFromCocoaFlags(cocoaFlags: NSEvent.ModifierFlags.command)
let keyCode = kVK_ANSI_R
var gMyHotKeyID = EventHotKeyID()
gMyHotKeyID.id = UInt32(keyCode)
// Not sure what "swat" vs "htk1" do.
gMyHotKeyID.signature = OSType("swat".fourCharCodeValue)
// gMyHotKeyID.signature = OSType("htk1".fourCharCodeValue)
var eventType = EventTypeSpec()
eventType.eventClass = OSType(kEventClassKeyboard)
eventType.eventKind = OSType(kEventHotKeyReleased)
// Install handler.
InstallEventHandler(GetApplicationEventTarget(), {
(nextHanlder, theEvent, userData) -> OSStatus in
// var hkCom = EventHotKeyID()
// GetEventParameter(theEvent,
// EventParamName(kEventParamDirectObject),
// EventParamType(typeEventHotKeyID),
// nil,
// MemoryLayout<EventHotKeyID>.size,
// nil,
// &hkCom)
NSLog("Command + R Released!")
return noErr
/// Check that hkCom in indeed your hotkey ID and handle it.
}, 1, &eventType, nil, nil)
// Register hotkey.
let status = RegisterEventHotKey(UInt32(keyCode),
modifierFlags,
gMyHotKeyID,
GetApplicationEventTarget(),
0,
&hotKeyRef)
assert(status == noErr)
}
}
A quick Swift 3 update for the setup:
let opts = NSDictionary(object: kCFBooleanTrue, forKey: kAXTrustedCheckOptionPrompt.takeUnretainedValue() as NSString) as CFDictionary
guard AXIsProcessTrustedWithOptions(opts) == true else { return }
NSEvent.addGlobalMonitorForEvents(matching: .keyDown, handler: self.handler)
I maintain this Swift package that makes it easy to both add global keyboard shortcuts to your app and also let the user set their own.
import SwiftUI
import KeyboardShortcuts
// Declare the shortcut for strongly-typed access.
extension KeyboardShortcuts.Name {
static let toggleUnicornMode = Self("toggleUnicornMode")
}
#main
struct YourApp: App {
#StateObject private var appState = AppState()
var body: some Scene {
WindowGroup {
// …
}
Settings {
SettingsScreen()
}
}
}
#MainActor
final class AppState: ObservableObject {
init() {
// Register the listener.
KeyboardShortcuts.onKeyUp(for: .toggleUnicornMode) { [self] in
isUnicornMode.toggle()
}
}
}
// Present a view where the user can set the shortcut they want.
struct SettingsScreen: View {
var body: some View {
Form {
HStack(alignment: .firstTextBaseline) {
Text("Toggle Unicorn Mode:")
KeyboardShortcuts.Recorder(for: .toggleUnicornMode)
}
}
}
}
SwiftUI is used in this example, but it also supports Cocoa.
Take a look at the HotKey Library. You can simply use Carthage to implement it into your own app.
HotKey Library
there is a pretty hacky, but also pretty simple workaround if your app has a Menu:
add a new MenuItem (maybe call it something like "Dummy for Hotkey")
in the attributes inspector, conveniently enter your hotkey in the Key Equivalent field
set Allowed when Hidden, Enabled and Hidden to true
link it with an IBAction to do whatever your hotkey is supposed to do
done!