Replaykit window doesnt dismiss when pressing cancel button in Swift? - ios

Im using replay kit to record my screen and when the preview screen pops up theres a cancel button to dismiss the screen but it doesn't do anything. I have the delegate func previewControllerDidFinish with the code to dismiss it but it doesn't go away. Does anyone know how to dismiss the window when pressing cancel? Thanks!
func previewControllerDidFinish(previewController: RPPreviewViewController) {
print("Preview finish")
// close preview window
previewController.dismissViewControllerAnimated(true, completion: nil)
}

In swift 3.2
First:
class ViewController: UIViewController, RPPreviewViewControllerDelegate {
func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
previewController.dismiss(animated: true, completion: nil)
}
}
Then when the stop recording button called, and set delegate in closure
if RPScreenRecorder.shared().isRecording {
RPScreenRecorder.shared().stopRecording { (previewController: RPPreviewViewController?, error: Error?) in
if previewController != nil {
let alertController = UIAlertController(title: "Recoring", message: "Do you wish to discard or view your recording?", preferredStyle: .alert)
let discardAction = UIAlertAction(title: "Discard", style: .destructive, handler: nil)
let viewAction = UIAlertAction(title: "View", style: .default, handler: { (action: UIAlertAction) in
// set delegate here
previewController?.previewControllerDelegate = self
self.present(previewController!, animated: true, completion: nil)
})
alertController.addAction(discardAction)
alertController.addAction(viewAction)
self.present(alertController, animated: true, completion: nil)
}
}
}

Try to implement this delegate
- (void)previewController:(RPPreviewViewController *)previewController didFinishWithActivityTypes:(nonnull NSSet<NSString *> *)activityTypes {
[previewController dismissViewControllerAnimated:YES completion:^{
}];
}

Related

Unable to dismiss UIViewController when it is presenting UIAlertController

I am trying dismiss a UIViewController that is currently presenting a UIAlertController as follows,
class SampleViewController: UIViewController {
private var alertController: UIAlertController?
override func viewDidLoad() {
super.viewDidLoad()
presentAlert()
}
fucn presentAlert() {
let alertController = UIAlertController(title: "alert", message: nil, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: .destructive, handler: {
dismiss(animated: true)
}))
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alertController, animated: true, completion: {
self.alertController = alertController
})
}
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
// alertController?.dismiss(animated: true)
/*This dismisses SampleViewController when user taps Cancel
but I want user to stay on this screen when they tap Cancel*/
super.dismiss(animated: flag, completion: completion)
}
}
class currentViewController: UIViewController {
private let sampleViewController = SampleViewController()
func presentSampleViewController() {
present(sampleViewController, animated: true)
}
func dismissSampleViewController() {
sampleViewController.dismiss(animated: true)
}
}
But only the alert gets dismissed not the entire SampleViewController also I couldn't find if override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) is called from CurrentViewController or UIAlertController action.
I would like to dismiss both alert and SampleViewController together and take the user back to CurrentViewController. Any help would be highly appreciated, thanks in advance.
Call dismiss(animated: flag, completion: completion) in dismissSampleViewController() instead of sampleViewController.dismiss(animated: true)
By dismissing CurrentViewController you'll dismiss all of it's children view controllers

ViewController dismiss swift ios

Hello my english is not very good, I apologize in advance.
I have a problem with my application. The scenario is as follows I have my rootViewController assigned to my ViewController controller which is my start. I have other screens that are a description screen where I have two login and registration buttons which when preloaded bring me to their controller.
Now, when I am on the log full screen form and I send the dismiss order:
ViewController registration
self.dismiss(animated: false, completion: nil)
And all ok the view is hidden but when entering the previous screen that was the description I have a validation if there is already a user if there is the dismiss order:
ViewController Description App
self.dismiss(animated: false, completion: nil)
But it does not perform the action.
Code
UIViewController
class ViewController: UIViewController {
override func viewDidLoad() {
FIRAuth.auth()!.addStateDidChangeListener() { auth, user in
if user == nil {
let descriptionController = DescriptionController()
present(descriptionController, animated: true, completion: nil)
}
}
}
}
DescriptionController
class DescriptionController: UIViewController {
#IBOutlet weak var sign_in_custom: UIButton!
override func viewDidLoad() {
FIRAuth.auth()!.addStateDidChangeListener() { auth, user in
if user != nil {
self.dismiss(animated: false, completion: nil)
}
}
sign_in_custom.addTarget(self, action: #selector(changeToSingIn), for: [.touchUpInside])
}
func changeToSingIn() {
let singInController = SingInController()
present(singInController, animated: true, completion: nil)
}
}
SingInController
class SingInController: UIViewController {
#IBOutlet weak var sign_in_custom: UIButton!
override func viewDidLoad() {
sign_in_custom.addTarget(self, action: #selector(singIn), for: [.touchUpInside])
}
func showLoad() {
let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)
alert.view.tintColor = UIColor.black
let loadingIndicator: UIActivityIndicatorView = UIActivityIndicatorView(frame: CGRectMake(10, 5, 50, 50) ) as UIActivityIndicatorView
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
present(alert, animated: true, completion: nil)
}
func hideLoad() {
self.dismiss(animated: false, completion: nil)
}
func singIn() {
if (emailVarification()){
if (passwordVarification()){
showLoad()
guard let email = emailTextField.text else { return }
guard let password = passwordTextField.text else { return }
FIRAuth.auth()?.createUser(withEmail: email, password: password) { (user, error) in
hideLoad()
if (user != nil) {
self.dismiss(animated: false, completion: nil)
} else {
let alert = UIAlertController(title: "Error", message: "This is a error", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
} else {
let alert = UIAlertController(title: "Error", message: "This is a error", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
} else {
let alert = UIAlertController(title: "Error", message: "This is a error", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
}
sequence
This is because the second controller has basically just been placed on the top of the existing one. The first view is still running under the second view, and when the second view is dismissed the first view won't call ViewDidLoad. So to solve it, you probably want to add it inside the ViewDidAppear Function.
Use this code in ViewdidAppear:
FIRAuth.auth()!.addStateDidChangeListener() { auth, user in
if user != nil {
self.dismiss(animated: false, completion: nil)
}
}
sign_in_custom.addTarget(self, action: #selector(changeToSingIn), for: [.touchUpInside])
Instead of having the DescriptionController dismiss itself, a better way would be for it would be to instead inform the ViewController that the user has signed in and also returns any errors, if necessary. Then the ViewController can perform any additional steps needed based on successful or failed sign-in. This could be accomplished by using the delegate pattern (DescriptionController defines a protocol and ViewController implements it).

How to access the view controller which is evoked by the stopRecording() method of ReplayKit framework. And how to save the video in my camera roll

How to access the view controller which is evoked by the stopRecording() method of ReplayKit framework. And how to save the video in the camera roll?
Try this if you haven't gotten to work yet:
func stopRecording() {
let sharedRecorder = RPScreenRecorder.sharedRecorder()
sharedRecorder.stopRecordingWithHandler { (previewViewController: RPPreviewViewController?, error: NSError?) in
if previewViewController != nil {
print("stopped recording")
previewViewController!.previewControllerDelegate = self
let alertController = UIAlertController(title: "Recording", message: "Tap view to watch, edit, share, or save your screen recording!", preferredStyle: .Alert)
let viewAction = UIAlertAction(title: "View", style: .Default, handler: { (action: UIAlertAction) -> Void in
self.view?.window?.rootViewController?.presentViewController(previewViewController!, animated: true, completion: nil)
})
alertController.addAction(viewAction)
self.previewViewController = previewViewController!
self.previewViewController.modalPresentationStyle = UIModalPresentationStyle.FullScreen
self.view?.window?.rootViewController!.presentViewController(alertController, animated: true, completion: nil)
} else {
print("recording stopped working")
//create the alert
let alert = UIAlertController(title: "Alert", message: "Sorry, there was an error recording your screen. Please Try Again!", preferredStyle: UIAlertControllerStyle.Alert)
// show the alert
self.view!.window?.rootViewController!.presentViewController(alert, animated: true, completion: nil)
alert.addAction(UIAlertAction(title: "Try Again!", style: UIAlertActionStyle.Destructive, handler: { action in
// add action
}))
}
}
}
internal func previewControllerDidFinish(previewController: RPPreviewViewController) {
self.previewViewController.dismissViewControllerAnimated(true, completion: nil)
print("cancel and save button pressed")
}

Dismissing old UIAlertViewController before presenting new UIAlertViewController

I am new to swift, i want to dismiss the alert which is present on
screen when the new alert comes.
I tried:
func showDefaultAlert(controller: UIViewController, title: String, message: String) {
// create the alert
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
// add an action (button)
alert.addAction(UIAlertAction(title: defaultTextForNormalAlertButton, style: UIAlertActionStyle.Default, handler: nil))
// show the alert
//controller.presentViewController(alert, animated: true, completion: nil)
self.showAlert(controller, alert: alert)
}
func showAlert(controller: UIViewController, alert: UIAlertController) {
if let currentPresentedViewController = UIApplication.sharedApplication().keyWindow?.rootViewController?.presentedViewController {
if currentPresentedViewController.isKindOfClass(UIAlertController) {
currentPresentedViewController.dismissViewControllerAnimated(false, completion: {
controller.presentViewController(alert, animated: true, completion: nil)
})
}else {
controller.presentViewController(alert, animated: true, completion: nil)
}
}
}
}
// Call to above method in view controller class:
SPSwiftAlert.sharedObject.showDefaultAlert(self, title:"Title1", message1: "Message")
SPSwiftAlert.sharedObject.showDefaultAlert(self, title:"Title2", message: "Message2")
-
but the above code giving the run time error as :
Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UIAlertController: 0x7fceb95dcfb0>)
After presenting the UIAlertController you can check its visibility as below:
Presented UIAlertController:
let alerts=UIAlertController(title: "Test", message: "Test", preferredStyle: .Alert)
presentViewController(alerts, animated: true, completion: nil)
Check if the UIAlertController is visible:
if (alerts.isViewLoaded())
{
print("Visible")
//Here you can dismiss the controller
//dismissViewControllerAnimated(true, completion: nil)
}
Check out this demo: Source code

AlertController is not in the window hierarchy

I've just created a Single View Application project with ViewController class. I would like to show a UIAlertController from a function which is located inside my own class.
Here is my class with an alert.
class AlertController: UIViewController {
func showAlert() {
var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert)
self.presentViewController(alert, animated: true, completion: nil)
}
}
Here is ViewController which executes the alert.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func showAlertButton(sender: AnyObject) {
var alert = AlertController()
alert.showAlert()
}
}
This is what I get instead of a beautiful alert.
Warning: Attempt to present UIAlertController: 0x797d2d20 on Sprint1.AlertController: 0x797cc500 whose view is not in the window hierarchy!
What should I do?
If you're instancing your UIAlertController from a modal controller, you need to do it in viewDidAppear, not in viewDidLoad or you'll get an error.
Here's my code (Swift 4):
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let alertController = UIAlertController(title: "Foo", message: "Bar", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
present(alertController, animated: true, completion: nil)
}
Let's look at your view hierarchy. You have a ViewController.
Then you are creating an AlertController, you are not adding it to your hierarchy and you are calling an instance method on it, that attempts to use the AlertController as presenting controller to show just another controller (UIAlertController).
+ ViewController
+ AlertController (not in hierarchy)
+ UIAlertController (cannot be presented from AlertController)
To simplify your code
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func showAlertButton(sender: AnyObject) {
var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert)
self.presentViewController(alert, animated: true, completion: nil)
}
}
This will work.
If you need the AlertController for something, you will have to add it to the hierarchy first, e.g. using addChildViewController or using another presentViewController call.
If you want the class to be just a helper for creating alert, it should look like this:
class AlertHelper {
func showAlert(fromController controller: UIViewController) {
var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert)
controller.presentViewController(alert, animated: true, completion: nil)
}
}
called as
var alert = AlertHelper()
alert.showAlert(fromController: self)
You can use below function to call alert from any where just include these method in AnyClass
class func topMostController() -> UIViewController {
var topController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController
while ((topController?.presentedViewController) != nil) {
topController = topController?.presentedViewController
}
return topController!
}
class func alert(message:String){
let alert=UIAlertController(title: "AppName", message: message, preferredStyle: .alert);
let cancelAction: UIAlertAction = UIAlertAction(title: "OK", style: .cancel) { action -> Void in
}
alert.addAction(cancelAction)
AnyClass.topMostController().present(alert, animated: true, completion: nil);
}
Then call
AnyClass.alert(message:"Your Message")
Write the following 3 lines, all we need to do is this.
Swift 3.0
private func presentViewController(alert: UIAlertController, animated flag: Bool, completion: (() -> Void)?) -> Void {
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: flag, completion: completion)
}
Swift 2.0
private func presentViewController(alert: UIAlertController, animated flag: Bool, completion: (() -> Void)?) -> Void {
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alert, animated: flag, completion: completion)
}
If you want to create a separate class for displaying alert like this, subclass NSObject not UIViewController.
And pass the ViewControllers reference from which it is initiated, to the showAlert function so that you can present alert view there.
Here is the code of an UIAlertController in a Utility.swift class (not a UIViewController) in Swift3, Thanks Mitsuaki!
private func presentViewController(alert: UIAlertController, animated flag: Bool, completion: (() -> Void)?) -> Void {
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: flag, completion: completion)
}
func warningAlert(title: String, message: String ){
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: { (action) -> Void in
}))
// self.present(alert, animated: true, completion: nil)
presentViewController(alert: alert, animated: true, completion: nil)
}
let alert = UIAlertController(title: "", message: "YOU SUCCESSFULLY\nCREATED A NEW\nALERT CONTROLLER", preferredStyle: .alert)
func okAlert(alert: UIAlertAction!)
{
}
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: okAlert))
let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
let window = windowScene?.windows.first
var rootVC = window?.rootViewController
if var topController = rootVC
{
while let presentedViewController = topController.presentedViewController
{
topController = presentedViewController
}
rootVC = topController
}
rootVC?.present(alert, animated: true, completion: nil)
It helped me to stick a slight delay between the viewDidLoad method and firing the alert method:
[self performSelector:#selector(checkPhotoPermission) withObject:nil afterDelay:0.1f];
This worked for me:
- (UIViewController *)topViewController{
return [self topViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController *)topViewController:(UIViewController *)rootViewController
{
if (rootViewController.presentedViewController == nil) {
return rootViewController;
}
if ([rootViewController.presentedViewController isMemberOfClass:[UINavigationController class]]) {
UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [self topViewController:lastViewController];
}
UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
return [self topViewController:presentedViewController];
}
Implementation:
UIViewController * topViewController = [self topViewController];
Using with alert:
[topViewController presentViewController:yourAlert animated:YES completion:nil];
You can send an alert from any class in your app (that uses UIKit: #import <UIKit/UIKit.h> )
Source here.
// I always find it helpful when you want to alert from anywhere it's codebase
// if you find the error above mentioned in the question' title.
let controller = UIAlertController(title: "", message: "Alert!", preferredStyle: UIAlertController.Style.alert)
let action = UIAlertAction(title: "Cancel" , style: UIAlertAction.Style.cancel, handler: nil)
controller.addAction(action)
// Find Root View Controller
var rootVC = UIApplication.shared.windows.first?.rootViewController
if var topController = rootVC {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
rootVC = topController
}
rootVC?.present(controller, animated: true, completion: nil)

Resources