How can i dismiss view controller if internet connected - ios

i am trying to dismiss view controller if internet connected. When there is no internet it present my noInternetViewController but when i reconnect to internet, noInternetViewController view didnt dismissed.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: ReachabilityChangedNotification, object: reachability)
}
func reachabilityChanged(note: NSNotification) {
let reachability = note.object as! Reachability
if reachability.isReachable {
if noInternet == true {
DispatchQueue.main.async {
self.noInternet = false
self.dismiss(animated: true, completion: nil)
}
}
} else {
noInternet = true
if noInternet == true {
DispatchQueue.main.async {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let noInternetViewController = storyboard.instantiateViewController(withIdentifier: "NoInternetViewController") as! NoInternetViewController
noInternetViewController.modalPresentationStyle = UIModalPresentationStyle.overFullScreen
self.present(noInternetViewController, animated: true, completion: nil)
}
}
}
}
And NoInternetViewController:
import UIKit
class NoInternetViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
Thanks for your helps

In your NoInternetViewController, you can add a Observer to it as well. When you received connection, post a notification to trigger it's selector to dismiss itself.
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(receivedConnection), name: NSNotification.Name.init(rawValue: "ReceivedConnection"), object: nil)
}
func receivedConnection() {
self.dismiss(animated: true, completion: nil)
}

Related

Back button does not work in Navigation Controller

I try to implement a back button for my controllers.
My Navigation Controller implementation inside of the Container Controller:
func configureMainController(){
let mainController = MainController()
mainController.delegate = self
mainController.backDelegate = self
centerController = UINavigationController(rootViewController: mainController)
view.addSubview(centerController.view)
addChild(centerController)
centerController.didMove(toParent: self)
}
My Back Delegate:
extension ContainerController: BackDelegate {
func handleBack() {
print("ok")
centerController.navigationController?.popViewController(animated: true)
}
}
Back button inside of the Main Controller:
navigationItem.rightBarButtonItem = UIBarButtonItem(image: image2?.withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(backAction))
#objc func backAction() -> Void {
backDelegate?.handleBack()
}
How I push the Controller that I need to pop:
DispatchQueue.main.async {
let vc = ScienceController(collectionViewLayout: UICollectionViewFlowLayout())
vc.modalPresentationStyle = .fullScreen
self.navigationController?.pushViewController(vc, animated: true)
}
Here is my Main Controller with relevant functions:
class MainController: UIViewController {
let tabBarCnt = UITabBarController()
var delegate: MainControllerDelegate?
var backDelegate: BackDelegate?
override func viewDidLoad() {
super.viewDidLoad()
createTabBarController()
}
#objc func backAction() -> Void {
//self.navigationController?.popViewController(animated: true)
backDelegate?.handleBack()
}
func checkIfUserIsLoggedIn(){
if Auth.auth().currentUser?.uid == nil{
performSelector(inBackground: #selector(handleLogout), with: nil)
}
}
#objc func handleLogout(){
do {
try Auth.auth().signOut()
} catch let logoutError {
print(logoutError)
}
DispatchQueue.main.async {
// UIView usage
let loginController = LoginController()
loginController.mainController = self
loginController.modalPresentationStyle = .fullScreen
self.present(loginController, animated: true, completion: nil)
}
}
func createTabBarController() {
let firstVc = Controller1()
firstVc.title = "vc1"
firstVc.view.backgroundColor = UIColor.white
firstVc.tabBarItem = UITabBarItem.init(title: "vc1", image: nil, tag: 0)
let secondVc = Controller2()
secondVc.title = "vc2"
secondVc.view.backgroundColor = UIColor.white
secondVc.tabBarItem = UITabBarItem.init(title: "vc2", image: nil, tag: 1)
let thirdVc = Controller3()
thirdVc.title = "vc3"
thirdVc.view.backgroundColor = UIColor.white
thirdVc.tabBarItem = UITabBarItem.init(title: "vc3", image: nil, tag: 2)
let controllerArray = [firstVc, secondVc, thirdVc]
tabBarCnt.viewControllers = controllerArray.map{
UINavigationController.init(rootViewController: $0)}
//tabBarCnt.selectedIndex = 1
self.view.addSubview(tabBarCnt.view)
}
}
Here is my Container Controller:
class ContainerController: UIViewController {
// MARK: - Properties
var menuController: MenuController!
var centerController: UINavigationController!
var isExpanded = false
// MARK: - Init
override func viewDidLoad() {
super.viewDidLoad()
configureMainController()
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .darkContent
}
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation{
return .slide
}
override var prefersStatusBarHidden: Bool{
return isExpanded
}
// MARK: - Handlers
func configureMainController(){
let mainController = MainController()
mainController.delegate = self
mainController.backDelegate = self
centerController = UINavigationController(rootViewController: mainController)
view.addSubview(centerController.view)
addChild(centerController)
centerController.didMove(toParent: self)
}
func configureMenuController(){
if menuController == nil{
menuController = MenuController()
menuController.delegate = self
view.insertSubview(menuController.view, at: 0)
addChild(menuController)
menuController.didMove(toParent: parent.self)
}
}
func animatePanel(shouldExpand: Bool, menuOption: MenuOption?){
if shouldExpand{
//show
UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveEaseInOut, animations: {
self.centerController.view.frame.origin.x = self.centerController.view.frame.width - 80
}, completion: nil)
}
else{
//hide
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseInOut, animations: {
self.centerController.view.frame.origin.x = 0
}) { (_) in
guard let menuOption = menuOption else {return}
self.didSelectMenuOption(menuOption: menuOption)
}
}
animateStatusBar()
}
func didSelectMenuOption(menuOption: MenuOption){
switch menuOption {
case .Profile:
let controller = ProfileController()
present(UINavigationController(rootViewController: controller), animated: true, completion: nil)
case .Settings:
let controller = SettingsController()
//controller.username = "pasha"
present(UINavigationController(rootViewController: controller), animated: true, completion: nil)
case .Logout:
handleLogout()
}
}
func animateStatusBar(){
UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveEaseInOut, animations: {
self.setNeedsStatusBarAppearanceUpdate()
}, completion: nil)
}
#objc func handleLogout(){
do{
try Auth.auth().signOut()
DispatchQueue.main.async {
UIApplication.shared.keyWindow?.rootViewController = LoginController()
}
//UIApplication.shared.keyWindow?.rootViewController?.dismiss(animated: true, completion: nil)
} catch let logoutError {
print(logoutError)
}
}
}
extension ContainerController: MainControllerDelegate {
func handleMenuToggle(forMenuOption menuOption: MenuOption?) {
if !isExpanded {
configureMenuController()
}
isExpanded = !isExpanded
animatePanel(shouldExpand: isExpanded, menuOption: menuOption)
}
}
extension ContainerController: BackDelegate {
func handleBack() {
print("ok")
centerController.popViewController(animated: true)
}
}
The print("ok") statement works but not centerController.navigationController?.popViewController(animated: true). I also tried self.navigationController?.popViewController(animated: true) and It does not work too...
Since UINavigationController is subclass of UIViewController it has navigationController property on it. You should not be using navigationController property present on UINavigationController. So your code should be
centerController.popViewController(animated: true)
Edit:
As "Sylvan D Ash" mentioned you are pushing and popping from 2 different controllers.
self.navigationController?.pushViewController(vc, animated: true)
need to be replaced with
centerController.pushViewController(vc, animated: true)

UIActivityViewController dismissing current view controller after sharing file

UIActivityViewController dismisses the presenting view controller after sharing files.
this is happening in iOS 13+ only. Is there any permanent solution for this?
Others apps seem to have this issue too after updating to iOS 13.
class VC : UIViewController {
#IBAction func moveFiles(_ sender: UIButton) {
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
alertController.addAction(UIAlertAction(title: "Move", style: .default, handler: { action in
let activityController = UIActivityViewController(activityItems: urls, applicationActivities: nil)
if (UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad) {
activityController.popoverPresentationController?.sourceRect = sender.frame
activityController.popoverPresentationController?.sourceView = sender.superview
}
self.present(activityController, animated: true, completion: nil)
}))
}
}
Here is the work around for your issue.
let tempController = TransparentViewController()
tempController.modalPresentationStyle = .overFullScreen
activityViewController.completionWithItemsHandler = { [weak tempController] _, _, _, _ in
if let presentingViewController = tempController?.presentingViewController {
presentingViewController.dismiss(animated: false, completion: nil)
} else {
tempController?.dismiss(animated: false, completion: nil)
}
}
present(tempController, animated: true) { [weak tempController] in
tempController?.present(activityViewController, animated: true, completion: nil)
}
Found similar question with solution which helps me. For iOS 13 show UIActivityViewController in another UIWindow
Stackoverflow answer
Seems it's fixed in iOS 14.4
For older iOS versions I found easier workaround. Override dismiss(animated:completion:) with empty implementation so it won't be dismissing itself automatically.
However, you can still dismiss this VC using super.dismiss(animated:completion).
E.g
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
// do nothing to workaround bug - automatically dimisses this VC after saveToCameraRoll activity was performed
// call super.dismiss(animated:completion:) in order to really dismiss this VC
// seems fixed in iOS 14.4
}
...
#objc private func didTapCloseButton(_ sender: UIButton) {
super.dismiss(animated: true) // calling parent class implementation
}
I have same issue right now on iOS target 14.1.
I made my solution based on answers I found.
final class ShareViewController: UIViewController {
private let activityItems: [Any]
private let applicationActivities: [UIActivity]?
// Same looking initializer as UIActivityViewController has
init(activityItems: [Any], applicationActivities: [UIActivity]? = nil) {
self.activityItems = activityItems
self.applicationActivities = applicationActivities
super.init(nibName: nil, bundle: nil)
// Make transparent and covering entire screen
view.backgroundColor = .clear
modalPresentationStyle = .overCurrentContext
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Present UIActivityViewController here
presentShareSheet()
}
fileprivate func presentShareSheet() {
let shareSheet = UIActivityViewController(activityItems: activityItems,
applicationActivities: applicationActivities)
shareSheet.completionWithItemsHandler = { [weak self] _, _, _, _ in
// This is necessary to dismiss parent VC
self?.dismiss(animated: false)
}
self.present(shareSheet, animated: true)
}
}
And use it like UIActivityViewController but presenting it without animations
#objc private func shareImage() {
guard let image = imageView.image else { return }
let shareVC = ShareViewController(activityItems: [image])
present(shareVC, animated: false)
}

Two textfield displays same GMSPlace Autocomplete

Im my GMSAUtocompleteController I have two textfield but they display same location when clicked
extension RideAddDetailsViewController: GMSAutocompleteViewControllerDelegate {
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
locationTextField.text = place.name
destinationTextField.text = place.name
dismiss(animated: true, completion: nil)
}
func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
// Handle the error
print("Error: ", error.localizedDescription)
}
func wasCancelled(_ viewController: GMSAutocompleteViewController) {
// Dismiss when the user canceled the action
dismiss(animated: true, completion: nil)
}
You can use tag to separate them.
if textField.isEqual(locationTextField)
{
let autocompleteController = GMSAutocompleteViewController()
autocompleteController.view.tag = 1 // assign the tag you want
autocompleteController.delegate = self
present(autocompleteController, animated: true, completion: nil)
}
else if textField.isEqual(destinationTextField)
{
let autocompleteController = GMSAutocompleteViewController()
autocompleteController.view.tag = 2 // assign the tag you want
autocompleteController.delegate = self
present(autocompleteController, animated: true, completion: nil)
}
Now you can separate the value from delegate method like this and assign to the textfield.
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
if viewController.view.tag == 1
{
locationTextField.text = place.name
}
else viewController.view.tag == 2
{
destinationTextField.text = place.name
}
dismiss(animated: true, completion: nil)
}
I set the reference of clicked textField to selectedTextField in both my IBActions.
var selectedTextField = UITextField()
#IBAction func locationTextFieldTapped(_ sender: Any) {
selectedTextField = locationTextField
//locationTextField.resignFirstResponder()
let autoCompleteController = GMSAutocompleteViewController()
locationTextField.tag = 0
autoCompleteController.delegate = self
present(autoCompleteController, animated: true, completion: nil)
}
#IBAction func destinationTextField(_ sender: Any) {
selectedTextField = destinationTextField
//destinationTextField.resignFirstResponder()
let autoCompleteController = GMSAutocompleteViewController()
destinationTextField.tag = 1
autoCompleteController.delegate = self
present(autoCompleteController, animated: true, completion: nil)
}
Then in the GMSAutocompleteViewControllerDelegate
I did this:
self.selectedTextField.text = place.name

There's a bug in CNContactPickerController

After spending all day (>12 hours) trying to isolate a bug in 13 lines of mind-bogglingly generic code, I have come to the dubious conclusion that there must be a bug in the current iteration of CNContactPickerViewController, in iOS 9.2.
Simply copy+paste this ViewController and link the invite action to a button.
The bug is that MFMessageComposeViewController dismisses itself immediately.
If anybody knows what to do with this, do share?
import UIKit
import MessageUI
import ContactsUI
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate, CNContactPickerDelegate {
let contactPickerVC = CNContactPickerViewController()
let messageVC = MFMessageComposeViewController()
override func viewDidLoad() {
super.viewDidLoad()
contactPickerVC.delegate = self
}
func contactPicker(picker: CNContactPickerViewController, didSelectContact contact: CNContact) {
if let phoneNumberValue = contact.phoneNumbers.first?.value as? CNPhoneNumber {
if let phoneNumber = phoneNumberValue.valueForKey("digits") as? String {
// Configure message ViewController
messageVC.messageComposeDelegate = self
messageVC.recipients = [phoneNumber]
messageVC.body = "Yoyoyo"
picker.presentViewController(messageVC, animated: true, completion: nil)
}
}
}
func messageComposeViewController(controller: MFMessageComposeViewController, didFinishWithResult result: MessageComposeResult) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func invite(sender: AnyObject) {
presentViewController(contactPickerVC, animated: true, completion: nil)
}
}
I got it working by dismissing the pickerVC and changing the controller which presents the messageVC!
Insert (before the messageVC config lines):
picker.dismissViewControllerAnimated(true, completion: nil)
Replace
picker.presentViewController(messageVC, animated: true, completion: nil)
with
presentViewController(messageVC, animated: true, completion: nil)

swift move view when keyboard appears - Bug

I am new developing with swift and don't know how to resolve my problem. I have two views. One for signUp and one for signIn. I added the following functions to my viewControllers and everything works fine. When I click on each textField (for password, username) the view moves up when keyboard appears. But when I sign in a user and after that call "Part 2" of my code and the User is signed in, the view changes from my signInView to my signUpView. The problem: after I did the following steps and tab now on my textFields the keyboard don't appear anymore.
Part 1: Code in signInViewController:
var kbHeight: CGFloat!
override func viewDidLoad() {
super.viewDidLoad()
textFieldUserName.delegate = self
txtFieldUserPassword.delegate = self
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
txtFieldUserPassword.resignFirstResponder()
return true
}
//viewDidAppear or viewWillAppear?
override func viewDidAppear(animated:Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
kbHeight = keyboardSize.height-50
self.animateTextField(true)
}
}
}
func keyboardWillHide(notification: NSNotification) {
self.animateTextField(false)
}
func animateTextField(up: Bool) {
let movement = (up ? -kbHeight : kbHeight)
UIView.animateWithDuration(0.3, animations: {
self.view.frame = CGRectOffset(self.view.frame, 0, movement) //Fatal Error: nil sometimes? - Why?
})
}
Part 2: Code in signInViewController
do {
try signup.signUpUser()
self.showAlert(self)
} catch ...
func showAlert(viewController : UIViewController) -> Void {
let alertController = UIAlertController(title: "Registrieren erfolgreich", message: "Sie können sich jetzt Anmelden", preferredStyle: .Alert)
alertController.addAction(UIAlertAction(title: "Anmelden", style: .Default, handler: { (alertAction) -> Void in self.dismissViewControllerAnimated(true, completion: nil) }))
viewController.presentViewController(alertController, animated: true, completion: nil)
I use this handy solution called TPKeyboardAvoiding in one of my apps: https://github.com/michaeltyson/TPKeyboardAvoiding
With this library you don't need handle this kind of scroll behavior on your app, imagine that you can have other behavior in other VC, and you need to calculate it every time.

Resources