Show window that covers everything when apps enters background - ios

In iOS12 and below I used to use something similar to this to show a window on top of everything to cover my app contents. This use to work but in iOS13 betas this does not work anymore.
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var coverWindow: UIWindow?
func applicationDidEnterBackground(_ application: UIApplication) {
if self.coverWindow != nil {
// Skip since cover window is already showing
return
}
let vc = UIViewController()
let label = UILabel(frame: window!.bounds)
label.text = "CoverWindow. Tap to app see contents"
vc.view = label
vc.view.backgroundColor = UIColor.lightGray
let coverWindow = UIWindow(frame: window!.bounds)
coverWindow.rootViewController = vc
coverWindow.windowLevel = .alert
coverWindow.makeKeyAndVisible()
self.coverWindow = coverWindow
}
}
Apparently window changes are not reflected in screen until app enters foreground again.
Question
Does anyone know how fix or workaround this? or maybe this approach is incorrect?
Any help would be highly appreciated
Notes
I don't use a simple view because my app might be showing other windows too and my requirement is to cover everything.
I don't use applicationWillResignActive because we want to only show coverWindow when it enters background. (TouchID authentication and other stuff might trigger applicationWillResignActive and coverWindow would incorrectly show)
Example code
Download Full working example code in Github (Run in iOS simulator 12 and 13 to see the difference)

You have to implement application life cycle, you just delete it , add those app life cycle functions and implement your codes , it ll be run without error

Answer to myself.
I reported this to Apple and it was fixed in iOS 13.1 or so. Latest version of iOS13 does NOT have this bug :)

Related

MFMailComposeViewController behaves differently in iOS 13 simulator and device

I'm trying to display MFMailComposeViewController in an app.
if MFMailComposeViewController.canSendMail() {
let mailComposeViewController = MFMailComposeViewController()
mailComposeViewController.navigationBar.tintColor = .white
mailComposeViewController.mailComposeDelegate = self
mailComposeViewController.setToRecipients(["support#gmail.com"])
mailComposeViewController.setSubject("Feedback")
present(mailComposeViewController, animated: true)
} else {
print("This device is not configured to send email. Please set up an email account.")
}
In iOS 12, it shows up without an issue. In both simulator and device.
But when I run the same project on a device running iOS 13, it looks like this.
The navigation bar color is gone. Also the send button is also invisible.
So I added mailComposeViewController.navigationBar.backgroundColor = .mv_primary but it still doesn't show on the device. Strangely the background color shows in the simulator.
However there's a strange behavior. The MFMailComposeViewController immediately dismisses by itself when I run it in the simulator.
The following error also shows up in the Xcode console.
[Common] [FBSSystemService][0x5f27] Error handling open request for
com.apple.MailCompositionService: {
userInfo = {
FBSOpenApplicationRequestID = 0x5f27;
}
underlyingError = ; } 2019-11-01
14:40:05.214158+0530 MailCompose[11289:262267] [Assert] Connection
request invalidated without resuming our _serviceSessionConnection.
This is an error. 2019-11-01 14:40:05.216901+0530
MailCompose[11289:262054] [General] #CompositionServices
_serviceViewControllerReady: NSError Domain=_UIViewServiceInterfaceErrorDomain Code=0
I guess the weird dismiss error is a Xcode bug. But how do I fix the background color and the send button not showing up in the device?
This is how I set all the navigationbar related styles.
UINavigationBar.appearance().barTintColor = .mv_primary
UINavigationBar.appearance().tintColor = .white
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
if #available(iOS 11.0, *) {
UINavigationBar.appearance().largeTitleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
}
Demo project
Add this line of code before present it. It will work normal. This a change in iOS 13
mailController.modalPresentationStyle = .fullScreen
The reason why the Mail composer dismisses immediately is because you can't actually send an email from the simulator. The implementation is different from iOS itself.
What I guess is happening here is that while the simulator implementation uses just some normal UI elements, the MFMailComposeViewController on native iOS is actually hosted like UIDocumentPickerViewController or UIActivityViewController. This means screenshots and trying to traverse the view tree is impossible, because the view is not an actual part of your application. They do that because these controllers contain user private information. Hosted view controller do NOT allow for customization, and do not comply with your global UINavigationBar.appearance(). This would explain why it does show up in the simulator and not on your native device.
This is new UI Style from iOS 13. You can disable it in Storyboard or set manual.
Presenting modal in iOS 13 fullscreen

UINAvigationController -> setViewControllers causes a crash

I've a very strange situation here - call of the method UINAvigationController -> setViewControllers:animated: causes a crash of the app. It's happining only on iOS 10.3.2 and when I'm building the app in release mode.
I've collected more details. Hope they can help to understand what happens.
The issue appears on iOS 10.3.2 and in release mode only. I’ve checked this on iPhone with 10.3.2 and release build fails but debug works OK. Additionally, I’ve checked previous version of the app from AppStore on iOS 10.3.2 and it’s OK too. Debug and release builds work fine on all of the previous versions of iOS.
The previous version in AppStore was built with older version of Xcode, and now I'm using latest Xcode 8.3.2. I suppose it's system issue, which is related to iOS and Xcode versions.
Regarding sources, it looks like:
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
...
window = UIWindow(frame: UIScreen.main.bounds)
....
let navigationController = UINavigationController(rootViewController: viewController)
window.rootViewController = navigationController
window.makeKeyAndVisible()
}
ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
...
continueButton.addTarget(self, action: #selector(navigateForward), for: .touchUpInside)
...
}
func navigateForward(sender: UIButton!) {
let nextController = FinalBuilder.viewController()
navigationController?.setViewControllers([nextController], animated: true)
}
I said before, it works fine in all cases except one :). UINAvigationController -> setViewControllers:animated: is standard iOS method, available from iOS 3.0+ and not deprecated now. There are no hacks or something else what can corrupt the program flow. And it’s usual way to use it.
P.S. There is no debug log or any other message which I can provide you because the app just disappears from the screen with no notification at all.
if its not work , so you can try easy way like
simple creat view controller object and pass in navigation
let nextVC = storyboard?.instantiateViewController(withIdentifier:"ScrollViewController") as! ScrollViewController
self.navigationController?.pushViewController(nextVC, animated: true)
I've found that this behavior appeared after update of RxCocoa from 3.3.1 to 3.4.0. It happens because of the following change in DelegateProxyType.swift : extension ObservableType : func subscribeProxyDataSource:
return Disposables.create { [weak object] in
subscription.dispose()
- unregisterDelegate.dispose()
object?.layoutIfNeeded()
+ unregisterDelegate.dispose()
}
I've posted report to ReactiveX/RxSwift repository. You can check final state there, if you are interested.

Swift: Animating a view (for in-app notifications) onto the screen for a few seconds before removing

I am attempting to do what is described perfectly here: https://github.com/bryx-inc/BRYXBanner
I want to create a banner that pops down on the screen for a few seconds before being removed (or removed when it is tapped on). The above project is great up until iOS 9. After that and with iOS 10, the banner no longer works as predicted and either shows itself without an animation for a third of a second or it doesn't show.
How can I add a view that animates onto the screen and then back off to provide a user a quick "No Internet" notification in-app. I want to avoid using the notification center.
I had a similar problem and created my own library for it: MDNotificationView
The example app on GitHub implements your idea. Here is a small snippet implementing it:
let view = MDNotificationCompactLayoutView()
view.textLabel.text = "No internet connection."
let notificationView = MDNotificationView(view: view)
notificationView.delegate = self
notificationView.show()
// MARK: - Notification View Delegate
func notificationDidShow(notificationView: MDNotificationView) {
// Hide the notification view automatically after 5 seconds.
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
notificationView.hide()
}
}

iOS: how to delay the launch screen?

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()
}

iPhone 6 Slide Out Menu

I am trying to make a collapsible slide out menu bar from scratch. I decided to try a UISplitViewController(). When I use an iPad simulator, I get a collapsible menu on the right side like desired. However, when I try it on my iPhone 6, the master view and detail view are shown on totally different screens instead of partially overlapping each other until a decision is made. Is it possible to get a split screen overlap when viewed on both the smaller devices (iPhone 5/6) and bigger devices?
The posted UISplitViewController is below:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.mainScreen().bounds)
let splitVC = UISplitViewController()
window?.rootViewController = splitVC
print(splitVC.collapsed)
var nav1 = UINavigationController(rootViewController: MasterViewControllerTableViewController())
var nav2 = UINavigationController(rootViewController: ViewController())
splitVC.viewControllers = [nav1, nav2]
splitVC.preferredDisplayMode = .PrimaryOverlay // this does not help?
window?.makeKeyAndVisible()
return true
}
I have built one from scratch but I would advise just using one of the pre-existing libraries. https://www.cocoacontrols.com/ is a good place to check. In the filter above select Sort: rating, Platform: iOS. You can even run the code online to test it. Most of the code for this controls is hosted on github.
If you do decide to build one there are plenty of tutorials if you google for “swift build slide out menu”. The code you have does a small subset of what you’ll need so there’s no simple “add this” or “change that” to get it to work.

Resources