Black Screen while sharing on Facebook with UIActivityViewController? - swift

Hi I am using UIActivityViewController to share content on social networks but facing one issue. The issue is when i select the share button the UIActivityViewController gets appear and I select the Facebook option.
I select the Facebook option in the sharing sheet -> Facebook sharing view appears with two options (Cancel and Next). When I select the Cancel button giving me another action sheet to discard or keep the post. When i select to discard the Action Sheet get disappear but the Facebook sharing view still keep showing and there is no way to go back.
Second when I press the next button in the Facebook sharing view -> another Facebook view gets appear with two buttons (Cancel and Share).... When I click the share button content get share on Facebook and black screen appears with a check in circle and text "Shared to Facebook!" .. And the application get stuck on this Screen.
Below is my code, Any help would be appreciated, I am also using the activityViewController!.completionWithItemsHandler but this closure not getting called in case of Facebook.
let url = "\(UserDefaults.baseURL)/pl/\(resource)/\(sharingId)"
// set up activity view controller
let imageToShare = [ url ]
activityViewController = UIActivityViewController(activityItems: imageToShare, applicationActivities: nil)
activityViewController!.popoverPresentationController?.sourceView = self.view // so that iPads won't crash
// exclude some activity types from the list (optional)
activityViewController!.excludedActivityTypes = [UIActivity.ActivityType.airDrop]
activityViewController!.completionWithItemsHandler = { (activityType: UIActivity.ActivityType?, completed:
Bool, arrayReturnedItems: [Any]?, error: Error?) in
if completed {
print("share completed")
self.dismiss(animated: true)
return
} else {
print("cancel")
self.dismiss(animated: true)
}
if let shareError = error {
print("error while sharing: \(shareError.localizedDescription)")
}
}
// present the view controller
self.present(activityViewController!, animated: true, completion: nil)

Related

Prevent open multiply tabs (Safari App Extension)

I'm currently working on a Safari App Extension for specific site site (example.com for example) and configure toolbar item to show popover. But if safari have not tab with loaded site.com then this site must be loaded in new tab. Code in SafariExtensionHandler
override func popoverWillShow(in window: SFSafariWindow) {
if SafariExtensionHandler.thePage == nil {
let theURL = URL(string: "https://example.com")
window.openTab(with: theURL, makeActiveIfPossible: true)
}
}
static property thePage get its value when handler receive first message from script, but there need some time for page loading and message from script will be send. While the page is loading second click on extensions toolbar item open new tab with example.com, next click open next tab... Block new tab opening I tried like this
override func popoverWillShow(in window: SFSafariWindow) {
if SafariExtensionHandler.thePage == nil && loadingInProgress != true {
loadingInProgress = true
let theURL = URL(string: "https://example.com")
window.openTab(with: theURL, makeActiveIfPossible: true)
}
}
Because all tabs in Safari lives in sandbox opning new tab reset all property of SafariExtensionHandler to it default value.
How to prevent open multiply tabs.
Thank you.

PHPickerViewController hide Search Bar and Navigation Bar

I've been trying to implement a photo selection feature in a new app. My current approach is to use a PHPickerViewController embedded in a UIViewControllerRepresentable to display in a swiftUI view.
This is my makeUIViewController function.
func makeUIViewController(context: Context) -> PHPickerViewController {
var configuration = PHPickerConfiguration()
configuration.filter = filter
configuration.selectionLimit = limit
let controller = PHPickerViewController(configuration: configuration)
controller.delegate = context.coordinator
return controller
}
It is inside a struct named PhotoPicker :
struct PhotoPicker: UIViewControllerRepresentable {
What I want to hide is this part :
Yes, all of that.
Let me explain myself, the PickerView is always presented, it is not a pop-up, so there is no need for a cancel button. As you can see there is no done button either. That's because only one image needs to be selected so what happens is when the user taps on an image, the event that a new image was selected is called immediately. Removing the need for user confirmation. Then concerning the search bar, I don't really want it, I just want the user to select a photo and finally the little switch between photos and albums isn't really necessary in my case either.
I've tried a lot of different ways, including trying to set options for the controller when it is created in makeUIViewController. These options were for example :
controller.navigationController?.setToolbarHidden(true, animated: false)
controller.navigationController?.setNavigationBarHidden(true, animated: false)
And I also tried invoking view modifier in my SwiftUI body :
PhotoPicker(filter: .images, limit: 1) { (results) in
}
.navigationBarTitle("")
.navigationBarHidden(true)
.statusBar(hidden: true)
.navigationBarBackButtonHidden(true)
But again, none of them seems to work. So that's why I'm asking here, because it seems I tried everything and nothing is working...

Sharing to Instagram using UIActivityView in SwiftUI

Im trying to share a Instagram post from an app that I made in swiftUI using UIActivityView, but whenever I try to share it only the picture shows up and the caption doesn't. The same thing happens with Facebook.
func sharePost(){
isSheetShowing.toggle()
let caption = "Hello, world!"
if let image = UIImage(named: "Image") {
let vc = UIActivityViewController(activityItems: [image,caption], applicationActivities: nil)
UIApplication.shared.windows.first?.rootViewController?.present(vc,animated : true)
}
}
UIActivityController needs to correctly recognize what exactly you are trying to share in order to display it corrrectly. This solution seems to answer your question directly.
Also, this is kind of an anti-pattern in SwiftUI (presenting it directly on the window). You should check this question out.

Performing a completion handler before app launches

I am attempting to open an app in one of two ways:
If the user has no UserDefaults saved, then open up a WelcomeViewController
If the user has UserDefaults saved, then open up a MenuContainerViewController as a home page
In step 2, if there are UserDefaults saved, then I need to log a user in using Firebase which I have through a function with a completion handler. If step 2 is the case, I want to open MenuContainerViewController within the completion block without any UI hiccups.
Here is the code I have currently:
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
FirebaseApp.configure()
guard
let email = UserDefaults.standard.string(forKey: "email"),
let password = UserDefaults.standard.string(forKey: "password")
else {
// User has no defaults, open welcome screen
let welcomeViewController = WelcomeViewController()
self.window?.rootViewController = welcomeViewController
self.window?.makeKeyAndVisible()
return true
}
// User has defaults saved locally, open home screen of app
let authentificator = Authentificator()
authentificator.login(with: email, password) { result, _ in
if result {
let menuContainerViewController = MenuContainerViewController()
self.window?.rootViewController = menuContainerViewController
self.window?.makeKeyAndVisible()
}
}
return true
}
Here is a video of the current UI, when I need to run the completion handler, the transition is not smooth into the app (there is a brief second with a black screen).
Please help me figure out how to make a smooth app launch.
I've had to handle situations similarly in my Firebase applications. What I typically do is make an InitialViewController. This is the view controller that is always loaded, no matter what. This view controller is initially set up to seamlessly look exactly like the launch screen.
This is what the InitialViewController looks like in the interface builder:
And this is what my launch screen looks like:
So when I say they look exactly the same, I mean they look exactly the same. The sole purpose of this InitialViewController is to handle this asynchronous check and decide what to do next, all while looking like the launch screen. You may even copy/paste interface builder elements between the two view controllers.
So, within this InitialViewController, you make the authentication check in viewDidAppear(). If the user is logged in, we perform a segue to the home view controller. If not, we animate the user onboarding elements into place. The gifs demonstrating what I mean are pretty large (dimension-wise and data-wise), so they may take some time to load. You can find each one below:
User previously logged in.
User not previously logged in.
This is how I perform the check within InitialViewController:
#IBOutlet var loginButton: UIButton!
#IBOutlet var signupButton: UIButton!
#IBOutlet var stackView: UIStackView!
#IBOutlet var stackViewVerticalCenterConstraint: NSLayoutConstraint!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
//When the view appears, we want to check to see if the user is logged in.
//Remember, the interface builder is set up so that this view controller **initially** looks identical to the launch screen
//This gives the effect that the authentication check is occurring before the app actually finishes launching
checkLoginStatus()
}
func checkLoginStatus() {
//If the user was previously logged in, go ahead and segue to the main app without making them login again
guard
let email = UserDefaults.standard.string(forKey: "email"),
let password = UserDefaults.standard.string(forKey: "password")
else {
// User has no defaults, animate onboarding elements into place
presentElements()
return
}
let authentificator = Authentificator()
authentificator.login(with: email, password) { result, _ in
if result {
//User is authenticated, perform the segue to the first view controller you want the user to see when they are logged in
self.performSegue(withIdentifier: "SkipLogin", sender: self)
}
}
}
func presentElements() {
//This is the part where the illusion comes into play
//The storyboard elements, like the login and signup buttons were always here, they were just hidden
//Now, we are going to animate the onboarding UI elements into place
//If this function is never called, then the user will be unaware that the launchscreen was even replaced with this view controller that handles the authentication check for us
//Make buttons visible, but...
loginButton.isHidden = false
signupButton.isHidden = false
//...set their alpha to 0
loginButton.alpha = 0
signupButton.alpha = 0
//Calculate distance to slide up
//(stackView is the stack view that holds our elements like loginButton and signupButton. It is invisible, but it contains these buttons.)
//(stackViewVerticalCenterConstraint is the NSLayoutConstraint that determines our stackView's vertical position)
self.stackViewVerticalCenterConstraint.constant = (view.frame.height / 2) + (stackView.frame.height / 2)
//After half a second, we are going to animate the UI elements into place
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
UIView.animate(withDuration: 0.75) {
self.loginButton.alpha = 1
self.signupButton.alpha = 1
//Create correct vertical position for stackView
self.stackViewVerticalCenterConstraint.constant = (self.view.frame.height - self.navigationController!.navigationBar.frame.size.height - self.signupButton.frame.maxY - (self.stackView.frame.size.height / 2)) / 3
self.view.layoutIfNeeded()
}
}
}

clearing variables of root view controller when switching tabs programmatically

I have a storyboard/root viewcontroller as the third tab on my app. On that tab the user selects an image, and writes a caption, which are stored as variables. From the root viewcontroller the user is taken to a preview screen via a segue which passes the variables, i.e. image and text. From this screen the user posts the object. From the post button, I am popping the viewcontroller, and programatically navigating to the first tab of my app. That all works, but the problem is when I then navigate back to the third tab, the stored variables are still there, and I'd like them to be cleared out.
I've seen many posts on keeping the variables, but none on how to reset them. I've tried to use viewWillDissapear but I don't want them cleared when I use the segue because I want the user to be able to go back and make some changes if needed.
UPDATED CODE WITH CORRECT ANSWER
#IBAction func postButtonPressed(_ sender: Any) {
PostFunction.createPost(image: self.postImage, postText: self.hashtag) { (true) in
self.tabBarController?.selectedIndex = 0
let tab3 = self.tabBarController!.viewControllers![2] as! UINavigationController
let vc = tab3.viewControllers.first as! PostHomeVC
vc.clear()
self.navigationController?.popViewController(animated: false)
}
}
Any suggestions on how to "reset" the rootviewcontroller from the above method?
Thanks!
I am popping the viewcontroller, and programatically navigating to the first tab of my app
before you do the programmatic switch to the first tab do
let tab3 = self.tabBarController.viewControllers[2] as! UINavigationController
let vc = tab3.viewControllers.first as! VCName
vc.clear()
or
let vc = self.navigationController!.viewControllers.first as! VCName
vc.clear()
and write that clear method inside the vc as you need