In my app, I have a dark launch screen and, hence, I would like to disable "smart" invert for the launch screen.
In a similar question, I found, that in any view, I can just do:
accessibilityIgnoresInvertColors = true
However, I obviously cannot get a hold of my launch screen's view.
Is there some hidden .plist setting, or any other steps I can take?
call below function in your willFinishLaunchingWithOptions of your AppDelegate :
func appAppearanceConfig(){
NotificationCenter.default
.addObserver(forName: UIAccessibility.invertColorsStatusDidChangeNotification,
object: nil,
queue: OperationQueue.main) {_ in
if UIAccessibility.isInvertColorsEnabled {
UIView.appearance().accessibilityIgnoresInvertColors = true
}
}
}
Related
I use AVFoundation for a video recording. When the app goes to the background I stop the capture session and when it enters the foreground I restart the capture session, everything works fine. I also use callKit to listen for incoming phone calls and that works fine too:
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterBackground), name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
#objc func appWillEnterBackground() {
// if recording stop recording, stop timer, etc ...
captureSession.stopRunning()
previewLayer = nil
}
#objc func appWillEnterForeground() {
if !captureSession.isRunning {
captureSession.startRunning()
initialize preview layer
}
}
The problem is while the vc with the camera is active (recording or not) and when I swipe from the bottom to bring up the Control Center Screen or swipe from the top to bring down the Notification Center Screen UIApplication.willResignActiveNotification gets called and the capture session is stopped. When I remove either of those screens UIApplication.willEnterForegroundNotification doesn't get called and the capture session is no longer running.
What I want to do is when either of those screens surface I simply use a bool to prevent the capture session from stopping
var haveControlScreensSurfaced = false // toggle this true/false depending when the control screens enter and leave
#objc func appWillEnterBackground() {
if view.window != nil && haveControlScreensSurfaced { return }
// if recording stop recording, stop timer, etc ...
captureSession.stopRunning()
previewLayer = nil
}
How can specifically listen for Control Center Screen and Notification Center Screen activity so that I can toggle my haveControlScreensSurfaced bool value to true/false?
This way works good for an avplayer
Since I needed a capture session long story short I used this. When sliding up/down either the Notification Center Screen or the Control Screen, UIApplication.didEnterBackgroundNotification (app enters background) and UIApplication.willEnterForegroundNotification (app is about to enter foreground) never get called. I simply moved my code to there and problem solved:
NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
#objc func didEnterBackground() {
// stop capture session
}
#objc func appWillEnterForeground() {
// start capture session
}
Here is a breakdown of what happens when the notifications are triggered:
Pressing the Home Button, sending app to the background:
1- `UIApplication.willResignActiveNotification` gets called first
2- `UIApplication.didEnterBackgroundNotification` gets called second // *** gets called when the HomeButton is pressed ***
Opening the app back up:
1- `UIApplication.willEnterForegroundNotification` gets called first // *** gets called when the opening the app back up ***
2- `UIApplication.didBecomeActiveNotification` gets called second
Sliding down the Notification Center Screen from the top:
1- `UIApplication.willResignActiveNotification` gets called first
2- `UIApplication.didBecomeActiveNotification` gets called second
3- if using the `.AVCaptureSessionWasInterrupted` the `.videoDeviceNotAvailableInBackground` gets called third
4- `UIApplication.willResignActiveNotification` gets called fourth
Sliding the Notification Center Screen back up:
1- `UIApplication.didBecomeActiveNotification` gets called alone
2- if using the `.AVCaptureSessionInterruptionEnded` it gets called second
Sliding the Control Screen up from the bottom:
1- `UIApplication.willResignActiveNotification` gets called alone
Sliding the Control Screen back back down:
1- `UIApplication.didBecomeActiveNotification` gets called by alone
I have a WKWebView in my UIViewController and I'm embedding a youtube video (using an iFrame) into the html along with text. When I launch the video it performs as intended, it opens in full screen, I can rotate either landscape orientations, plays fine etc. The problem lies when I close the video. My application is locked to portrait and when returning from the video the application is half black screen, half my application and the view that is still half my application looks to be in landscape (too larger for the width).
My application is locked portrait within the Info.plist for the whole application. I am fine with the video rotating it just causes this interesting outcome.
1. Launch WKWebView with youtube iframe
2. Click to launch video (video plays full screen)
3. Rotate device to either landscape rotations.
4. Close the player
This is where you notice that half the application is black and the other half looks to be portrait orientation in landscape layout.
When I inspect the views before and after it mimics as if the app has rotated to landscape mode but the device is in portrait. (View starts at 414x862. After viewing and rotating with the video and returning to the view it shows as 862x414)
I'm not really sure what's going on here. Thoughts?
Thanks in advance
I was able to find a workaround/hack/solution to this. I kept my application locked to portrait but overrode the AppDelegate method for allowed orientations.
class AppDelegate: UIResponder, UIApplicationDelegate {
...
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return OrientationManager.allowedOrientations
}
...
}
I created an OrientationManager which was just a helper class to allow me to force rotations and alter the allowed orientations.
class OrientationManager {
/// The currently allowed orientations of the application (default: .portrait)
static var allowedOrientations: UIInterfaceOrientationMask = .portrait
/**
* Method to force the current orientation of the device.
*
* - Parameter orientation: The orientation to change to.
*
* - Warning: This method uses setValue(_, forKey:) which accesses values that may change in the future
*/
static func forceOrientation(_ orientation: UIInterfaceOrientation) {
let orientationRawValue = orientation.rawValue
UIDevice.current.setValue(orientationRawValue, forKey: "orientation")
UINavigationController.attemptRotationToDeviceOrientation()
}
}
The last thing I needed to do was figure out when the video was going in and out of full screen. I found some notifications that seemed to reliably fire when the video goes in and out of full screen. When this happens I enable the ability for the view to rotate, which allows it to go to Landscape behind the full screen video. Once the video is closed my view is now (unfortunately) showing in landscape which I can then force the orientation and re-lock it to portrait.
In the UIViewController with the webview:
class WebViewWithVideoViewController: UIViewController {
...
// MARK: Properties
var videoDidFullScreenNotification: NSObjectProtocol?
var videoDidMinimizeNotification: NSObjectProtocol?
...
// MARK: Lifecycle
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.beginObserving()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.endObserving()
}
// MARK: Observers
func beginObserving() {
// This is a solution to the video causing a half black screen when the device is rotated to landscape.
// The outline of the solution is that when the video is put into full screen mode, we update the
// available orientations to include both landscape directions. Upon the video being closed,
// we force the device orientation back to portrait and then lock it back to portrait only.
// This seems to work because the UIViewController is actually in landscape once complete
// and then it animates back to the portrait orientation and seems to keep the proper ratios
// and sizes.
if self.videoDidFullScreenNotification == nil {
self.videoDidFullScreenNotification = NotificationCenter.default.addObserver(
forName: UIWindow.didBecomeVisibleNotification,
object: self.view.window,
queue: nil
) { notification in
OrientationManager.allowedOrientations = .allButUpsideDown
}
}
if self.videoDidMinimizeNotification == nil {
self.videoDidMinimizeNotification = NotificationCenter.default.addObserver(
forName: UIWindow.didBecomeHiddenNotification,
object: self.view.window,
queue: nil
) { notification in
OrientationManager.forceOrientation(.portrait)
OrientationManager.allowedOrientations = .portrait
}
}
}
func endObserving() {
if let videoDidFullScreenNotification = self.videoDidFullScreenNotification {
NotificationCenter.default.removeObserver(videoDidFullScreenNotification)
}
if let videoDidMinimizeNotification = self.videoDidMinimizeNotification {
NotificationCenter.default.removeObserver(videoDidMinimizeNotification)
}
}
...
}
This seemed to have solved the screen showing half black after going in and out of full screen with embedded videos. Unfortunately there is a slight animation when you return from the full screen video, but it's a small sacrifice for a very weird bug.
Hope this helps anyone else with this (or similar issues) and happy coding!
Given a UIViewController, I would like to receive text input only from the external keyboard. Think UIKeyCommand but for any character (not just 'modified' ones).
However, when I try to implement that using UIKeyInput, it seems that iOS desperately wants to display a keyboard if there is no external one connected.
Is there any way to circumvent that? Specifically, to have the options to receive keystrokes from the keyboard if, and only if, one is connected?
After fiddling with a iPad for an hour, I finally have a good solution for this in swift. The other methods are weak or use 3rd party software. The reason why UIKeyboardWillShowNotification is getting fired even when an external keyboard is being used for an iPad is the shortcut bar existing. In order to disable the shortcut bar, do this:
let item : UITextInputAssistantItem = textField.inputAssistantItem
item.leadingBarButtonGroups = []
item.trailingBarButtonGroups = []
This covers most cases of what you need, but UIKeyboardWillShowNotification can still be fired if someone plugs their keyboard in at certain points of use. If you have the screen adjust, you can't afford any case for the user to experience this. Plus, you might want the shortcut bar for some reason. Regardless of what your desires are, this covers all cases of an external keyboard being used:
Add to viewDidAppear
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(LoginViewController.keyboardWillShow), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(LoginViewController.keyboardWillHide), name: UIKeyboardWillHideNotification, object: nil)
whenever you leave the view add this to anything that makes you leave
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
also add it to the deinit{} method to be thourough.
Now use these functions:
func keyboardWillShow(notification: NSNotification) {
// This code is an alternate way of checking for keyboard
var userInfo: [NSObject: AnyObject] = notification.userInfo!
let firstFrame = userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue
let secondFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue
let firstRect = firstFrame.CGRectValue()
let secondRect = secondFrame.CGRectValue()
let diff = abs(firstRect.origin.y - secondRect.origin.y)
let isFirstBigger = firstRect.origin.y > secondRect.origin.y
if firstRect != secondRect && diff != 55 {
if !isFirstBigger {
//animateViewToDefaultPosition()
} else {
//animateViewToPositionWhenKeyboardActive()
}
}
}
func keyboardWillHide() {
//animateViewToDefaultPosition()
}
The 55 is the height of the shortcut bar. You can remove it's functionality if you don't have one. The !isFirstBigger is used to check for when they unhook the keyboard and hook it back in during text field editing. It is also important that diff != 55 during that check because with a shortcut bar this would be the case when you did not want to animate the screen.
This is by far the best method I have seen after scouring Stack Overflow. If anyone finds a bug in the functionality, let me know, but I am confident it will take care of the pesky shortcut bar external keyboard issues. I hope this helps everyone else out there confused by all this!
When launch an app, the LaunchScreen.xib is removed as soon as all the assets are initialized.
I want to make the launch screen stay for at least 1 sec.
Is there a way to achieve this?
Thank you!
You can create a view controller that uses the LaunchScreen storyboard, present it (not animated) on applicationDidFinishLaunching or applicationWillFinishLaunching, and dismiss it whenever you want.
Keep in mind this is discouraged by Apple because it gives the impression that your app takes a lot longer to launch, which is bad user experience and might cause some of your users to delete your app.
Swift 4 Update
Just write one line of code
Thread.sleep(forTimeInterval: 3.0)
in the method of didfinishLauching.... in appdelegate class.
Example
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
Thread.sleep(forTimeInterval: 3.0)
// Override point for customization after application launch.
return true
}
Never sleep on the main thread. Doing this could actually cause iOS to kill your app for taking too long to start up
Thought I chip in my thoughts on this, I wanted to write in comments but it won't allow many lines. I believe many apps developer want to do this (delay the launch screen) is because they want to build a brand presence of the apps/games for their company.
Having said that, launch screen is NOT designed for that, as Rick Maddy explained in the comment section in one of the other answers. Launch screen's purpose is to make users feel the app is instantly running by showing the empty UI while the actual data is loading at the back (willAppear and so on).
So to achieve what many developers want, while being in-line with Apple's HIG, what you can do is:
Display UI template in the launchscreen as intended by Apple HIG.
Upon main screen load, load up another VC that shows "intro" of your brand. Make sure this runs only ONCE (a simple flag in
NSUserDefaults should do the trick).
Users should be allowed to skip this if it is a long "intro".
The same "intro" VC should be available to user by tapping on a "View Intro" button somewhere (maybe in about page).
If you want to go with simple, you can use NSThread:
[NSThread sleepForTimeInterval:(NSTimeInterval)];
You can put this code into the first line of applicationDidFinishLaunching method.
For example, display default.png for 1.0 seconds.
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
[NSThread sleepForTimeInterval:1.0];
}
It will stop splash screen for 1.0 seconds.
Alhamdulellah Solution is find
Only copy and paste this code in AppDelegate Class
Call this SplashScreenTiming() in didFinishLaunchingWithOptions()
private func SplashScreenTiming(){
let LunchScreenVC = UIStoryboard.init(name: "LaunchScreen", bundle: nil)
let rootVc = LunchScreenVC.instantiateViewController(withIdentifier: "splashController")
self.window?.rootViewController = rootVc
self.window?.makeKeyAndVisible()
Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(DismissSpalshController), userInfo: nil, repeats: false)
}
#objc func DismissSpalshController(){
let mainVC = UIStoryboard.init(name: "Main", bundle: nil)
let rootvc = mainVC.instantiateViewController(withIdentifier: "SignInVC")
self.window?.rootViewController = rootvc
self.window?.makeKeyAndVisible()
}
I am trying to understand doing Quick Actions (3D Touch) for iOS 9.
I wanted the user to select 1 of 4 filter to be applied to image, so if I select item 1, I will set the NSUserDefaults.standardUserDefaults() to the filter, then show the correct picture with the applied filter.
In AppDelete.swift:
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
var filterType:Int
switch (shortcutItem.type) {
...set filterType
}
NSUserDefaults.standardUserDefaults().setInteger(filterType, forKey:"filterType")
NSUserDefaults.standardUserDefaults().synchronize()
}
In ViewController.swift:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector:"setDefaultFilter", name: UIApplicationWillEnterForegroundNotification, object:nil) // Handle enter from background
setDefaultFilter()
}
func setDefaultFilter() {
filterType = defaults.integerForKey("filterType")
...
imageView.image = filterImage(defaultImage!, filter:filters[filterType])
}
However, when enter the app from the menu, it will always show the last selection (not the current selection). If I select item 1, nothing happened. I select item 3, item 1 will appeared.
I have also try passing parameters via appDelegate and the result is the same. I believe there are some issues with life cycle.
Any ideas?
NSUserDefaults write data on flash, which may not be so fast.
You can wait a little longer, like observe UIApplicationDidBecomeActiveNotification other than UIApplicationWillEnterForegroundNotification.
Or you can use other ways to pass params, e.g., as an instance variable in AppDelegate.
didFinishLaunchingWithOptions method is always called before calling performActionForShortcutItem method to response to the quick action.
So, I think that you need to check what kind of quick action is selected in didFinishLaunchingWithOptions method. If the app is not launched from quick action, you just continue to your normal app launching process.(default filter)
And if you decide to handle quick action in didFinishLaunchingWithOptions, you have to return NO in didFinishLaunchingWithOptions.
You could get more idea from my demo project:
https://github.com/dakeshi/3D_Touch_HomeQuickAction