popover close from another view controller swift 3 - swift3

i work with Xcode 8 and swift 3.
i have a viewcontroller (Class User:NSViewcontroller) with a button.
This Button has an action:
#IBAction func btnAction(_ sender: Any) {
let popover = NSPopover()
popover.contentViewController = NSStoryboard(name: "Main", bundle: nil).instantiateController(withIdentifier: "Popover") as! NSViewController
popover.show(relativeTo: button.bunds , of: button, preferredEdge: .maxX)
}
This button open another view controller as popover.
this popover view controller (Class Popover:NSViewController) do some actions.
now my question is: how can I close the popover viewcontroller from the popover class?

Not sure about the above code but in order to create your PopoverView and dismiss it. Here are the steps:-
1) Create one new View Controller names as PopOverViewController.swift
2) Implement the below method inside that:-
func updatePopOverViewController(_ button: UIButton?, with delegate: AnyObject?) {
guard let button = button else { return }
modalPresentationStyle = .popover
popoverPresentationController?.permittedArrowDirections = [.up]
popoverPresentationController?.backgroundColor = view.backgroundColor
popoverPresentationController?.sourceView = button
popoverPresentationController?.sourceRect = button.bounds
popoverPresentationController?.delegate = OtherViewControllerClass
}
3) Inside your OtherViewControllerClass implement the following code:-
extension OtherViewControllerClass: UIPopoverPresentationControllerDelegate {
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.none
}
func popoverPresentationControllerDidDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) {
//your code for eg. if you want to change the tint color of our button
}
//When you tap on button it will show the popover and while tapping on yourViewcontroller view it will dismiss the popover accordingly.
#IBAction func buttonClicked(_ sender: UIButton) {
let viewController = PopOverViewController()
viewController.updatePopOverViewController(sender, with: self)
present(viewController, animated: true, completion: nil)
}
}

Well I might be totally guessing here as I have no idea about how your ViewController is written but this should fairly work
self.presentedViewController?.dismiss(animated: true, completion: nil)
The basic thing here is you can access the VC which presented the popover and then call dismiss over it.

Related

UIButton in SwiftUI Catalyst Mac app doesn't work when clicked second time

Here's the code I have:
private struct ShareButton: UIViewRepresentable {
func makeUIView(context: Context) -> UIButton {
let activityViewController = UIActivityViewController(activityItems: [URL(string: "https://www.apple.com/")!], applicationActivities: nil)
let action = UIAction(title: "Share") { _ in UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.rootViewController?.present(activityViewController, animated: false) }
let button = UIButton(primaryAction: action)
activityViewController.popoverPresentationController?.sourceView = button
return button
}
func updateUIView(_ uiView: UIButton, context: Context) { }
}
Basically it's creating a UIButton with a UIAction, inside which there's a UIActivityViewController that set sourceView for the share menu to be the UIButton.
Here's a demo of the issue:
The UIButton is created when the SwiftUI view is created, and set as the sourceView. My guess is that the issue occur because the UIButton is somehow destroyed and recreated due to some SwiftUI mechanism? I can be entirely wrong though. Anyway to solve this?
Or any other way to do share button in a SwiftUI Catalyst Mac app?
"Or any other way to do share button in a SwiftUI Catalyst Mac app?"
You could try this approach, using the extension from:
How to get rid of message " 'windows' was deprecated in iOS 15.0: Use UIWindowScene.windows on a relevant window scene instead" with AdMob banner?
public extension UIApplication {
func currentUIWindow() -> UIWindow? {
let connectedScenes = UIApplication.shared.connectedScenes
.filter({
$0.activationState == .foregroundActive})
.compactMap({$0 as? UIWindowScene})
let window = connectedScenes.first?
.windows
.first { $0.isKeyWindow }
return window
}
}
struct ContentView: View {
let holaTxt = "Hola 😀 "
var body: some View {
Button(action: {
let AV = UIActivityViewController(activityItems: [holaTxt], applicationActivities: nil)
UIApplication.shared.currentUIWindow()?.rootViewController?.present(AV, animated: true, completion: nil)
}) {
Text("Share")
}
}
}
Found an elegant solution for creating a share button in a SwiftUI Catalyst Mac app (in fact, all SwiftUI app), see https://github.com/SwiftUI-Plus/ActivityView

How to redirect the user to a specific view after clicking on a push notification in SwiftUI iOS 15 / Xcode 13 (no Storyboard)?

I have push notification on my app (works well) and I would like to redirect my users to my "Messages" view when they click on the notification, how to do that in SwiftUI (no Storyboard)?
I know that i have to implement the code in this function (in AppDelegate) :
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
}
My main view is a Tabview and "Messages" view is in it in a navigation View.
Thanks for your help!
So i think that we just have to modify these lines ...
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
// retrieve the root view controller (which is a tab bar controller)
guard let rootViewController = (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.window?.rootViewController else {
return
}
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// instantiate the view controller we want to show from storyboard
// root view controller is tab bar controller
// the selected tab is a navigation controller
// then we push the new view controller to it
if let conversationVC = storyboard.instantiateViewController(withIdentifier: "ConversationViewController") as? ConversationViewController,
let tabBarController = rootViewController as? UITabBarController,
let navController = tabBarController.selectedViewController as? UINavigationController {
// we can modify variable of the new view controller using notification data
// (eg: title of notification)
conversationVC.senderDisplayName = response.notification.request.content.title
// you can access custom data of the push notification by using userInfo property
// response.notification.request.content.userInfo
navController.pushViewController(conversationVC, animated: true)
}
// tell the app that we have finished processing the user’s action / response
completionHandler()
}
So i think that we can change :
guard let rootViewController = (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.window?.rootViewController else {
return
}
to
guard let rootViewController = UIApplication.shared.currentUIWindow()?.rootViewController else {
return
}
But for the rest I don't know how to make the changes

Wrapped SwiftUI View popping itself

I have a SwiftUI View wrapped in a UIHostingController that's then pushed onto the nav stack from a UIViewController.
It's pushed in this way:
let controller = TransactionDetailsHostingController(transactionRecord: record)
navigationController?.pushViewController(controller, animated: true)
I want a custom back button that will pop the view from the navstack and I tried the technique used here and elsewhere: SwiftUI - Is there a popViewController equivalent in SwiftUI?
Unfortunately, it doesn't work. The wrapped view doesn't pop from the navstack. I need a solution that works on iOS 13.
There are two approaches for pop to view the controller.
Use closure. Create closure inside the SwiftUI view and call from the controller.
Here is the demo.
struct SwiftUIView: View {
var onBack: () -> Void
var body: some View {
Button(action: {
onBack()
}, label: {
Text("Go to back")
})
}
}
class ViewController: UIViewController {
#IBAction func onNavigation(_ sender: UIButton) {
let controller = UIHostingController(rootView: SwiftUIView(onBack: {
self.navigationController?.popViewController(animated: true)
}))
navigationController?.pushViewController(controller, animated: true)
}
}
Find the top most controller and pop. You can find top controller from here
And then use like this
UIApplication.shared.topMostViewController()?.navigationController?.popViewController(animated: true)

How to fix a Picker navigation works only when I long press a link

I'm currently developing an application using SwiftUI.
When I use a onTapGesture at Form in NavigationView, navigation of Picker the inside doesn't work.
It works but only when I long-press a link like a LongPressGesture.
If I don't use onTapGesture, the navigation of Picker works, as usual, But in that case, I can not close a keyboard when I use TextField choosing numberPad as keyboardType...
How could I solve this problem?
Here is a code:
OnTapGestureTest.swift
import SwiftUI
struct OnTapGestureTest: View{
#State var inputNo:String = ""
#State var selectNo:Int = 0
var body: some View {
NavigationView{
Form{
TextField("InputNo", text: $inputNo)
.keyboardType(.numberPad)
Picker(selection:$selectNo, label: Text("SelectNo")){
Text("0").tag(0)
Text("1").tag(1)
Text("2").tag(2)
Text("3").tag(3)
Text("4").tag(4)
Text("5").tag(5)
}
}
.onTapGesture {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
}
}
UPDATED
I tried to solve my question by referencing here.
*I want to close keyboard only on Tap outside (without handling drags) in iOS 13.0
SceneDelegate.swift
...
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
...
let tapGesture = UITapGestureRecognizer(target: window, action:#selector(UIView.endEditing))
tapGesture.requiresExclusiveTouchType = false
tapGesture.cancelsTouchesInView = false
tapGesture.delegate = self
window?.addGestureRecognizer(tapGesture)
}
...
But it still doesn't work well... Do I need to do some more things? misunderstand the answer?
Xcode: Version 12.0.1
iOS: 13.0
You could add the tap gesture globally, with the help of UIWindow:
extension UIApplication {
func addTapGestureRecognizer() {
guard let window = windows.first else { return }
let tapGesture = UITapGestureRecognizer(target: window, action: #selector(UIView.endEditing))
tapGesture.requiresExclusiveTouchType = false
tapGesture.cancelsTouchesInView = false
tapGesture.delegate = self
window.addGestureRecognizer(tapGesture)
}
}
extension UIApplication: UIGestureRecognizerDelegate {
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
In your main: (The idea: as soon as your app launches, this tapGesture is added to the window)
#main
struct YourApp: App {
...
var body: some Scene {
WindowGroup {
//Your initial view
.onAppear(perform: UIApplication.shared.addTapGestureRecognizer)
}
}
}
This is especially convenient since you don't have to take care of hiding the keyboard ever again inside your views and it's not blocking any of your controls.
Note
This code is from Here
Also, I saw iOS13 - for non SwiftUI Cycle, this would be the equivalent: Here

Swift 3 UIButton addTarget not working

I have created a button in my UIView but can't figure out why it wont work. The same code works fine in other UIViews. Here is my code. I have tried different combinations of initializing the button with a frame or by using constraints but it makes no difference.
override func viewDidLoad() {
super.viewDidLoad()
let backButton: UIButton = {
let button = UIButton()
button.setTitle("BACK", for: .normal)
button.setTitleColor(Constants.APP_TEXT_COLOR, for: .normal)
button.titleLabel?.font = Constants.APP_HEADER_FONT
button.backgroundColor = .blue
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(pressBackButton(button:)), for: .touchUpInside)
return button
}()
backView.addSubview(backButton)
view.addSubview(backgroundView)
backgroundView.addSubview(webView)
backgroundView.addSubview(backView)
}
and my function
func pressBackButton(button: UIButton) {
print("BUTTON PRESSED")
}
It works fine when i try your code with one change as i don't have backView. So replaced
backView.addSubview(backButton)
with
self.view.addSubview(backButton)
I can see the log present in pressBackButton function on console on pressing the button. Make sure your console view is not disabled. shift+command+C shows the console view.