AutoLayout issue Swift 3.0 - swift3

I have already Autolayout this screenshot using.I want when i click textView,textView will always just above Keyboard and also i am using custom NavigationBar.I already used IQKeyBoardManagerSwiftIt is working but my NavigationBar also moves up I want my NavigationBarto be stick at top if i click textView.Any Solutions to this. thanks in advance

Swift 5.0 :- Drag your UITextView in a contentView(UIView), Create IBOutlet of bottom constraint of contentView i.e bottomConstraint. After use the below code as mentioned and custom NavigationBar will also stick at top only textView will be just above keyboard.
override func viewDidLoad() {
super.viewDidLoad()
let center: NotificationCenter = NotificationCenter.default
center.addObserver(self, selector: #selector(Profile.keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
center.addObserver(self, selector: #selector(Profile.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
#objc func keyboardWillShow(notification: NSNotification){
let userInfo:NSDictionary = notification.userInfo! as NSDictionary
let keyboardSizeNow:CGSize = (userInfo.object(forKey: UIKeyboardFrameEndUserInfoKey)! as AnyObject).cgRectValue.size
UIView.animate(withDuration: 0.2, animations: { () -> Void in
self.bottomConstraint.constant = keyboardSizeNow.height - 49
self.view.layoutIfNeeded()
})
}
#objc func keyboardWillHide(notification: NSNotification){
UIView.animate(withDuration: 0.2, animations: { () -> Void in
self.bottomConstraint.constant = 0
self.view.layoutIfNeeded()
})
}

you can implement keyboardWillShow and keyboardWillHide method in similar way
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
buttonBottomConstraint.constant = keyboardSize.height
UIView.animate(withDuration: 0.3, animations: {
self.view.layoutIfNeeded()
})
}
}
func keyboardWillHide(notification: NSNotification) {
if let _ = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
bottomConstraint.constant = 0
UIView.animate(withDuration: 0.3, animations: {
self.view.layoutIfNeeded()
})
}
}
also, don't forget to observe in viewDidLoad.

Related

Display Share Sheet (UIActivityViewController) in a Modal .sheet() in SwiftUI [duplicate]

I'm trying to present a UIActivityViewController (share sheet) from a SwiftUI View. I created a view called ShareSheet conformed to UIViewControllerRepresentable to configure the UIActivityViewController, but it's turning out to be not as trivial to actually present this.
struct ShareSheet: UIViewControllerRepresentable {
typealias UIViewControllerType = UIActivityViewController
var sharing: [Any]
func makeUIViewController(context: UIViewControllerRepresentableContext<ShareSheet>) -> UIActivityViewController {
UIActivityViewController(activityItems: sharing, applicationActivities: nil)
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ShareSheet>) {
}
}
Doing so naively via .sheet leads to the following.
.sheet(isPresented: $showShareSheet) {
ShareSheet(sharing: [URL(string: "https://example.com")!])
}
Is there a way to present this like it's usually presented? As in covering half the screen?
Hope this will help you,
struct ShareSheetView: View {
var body: some View {
Button(action: actionSheet) {
Image(systemName: "square.and.arrow.up")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 36, height: 36)
}
}
func actionSheet() {
guard let data = URL(string: "https://www.apple.com") else { return }
let av = UIActivityViewController(activityItems: [data], applicationActivities: nil)
UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true, completion: nil)
}
}
In iOS 14, Swift 5, Xcode 12.5 at least, I was able to accomplish this fairly easily by simply wrapping the UIActivityViewController in another view controller. It doesn't require inspecting the view hierarchy or using any 3rd party libraries. The only hackish part is asynchronously presenting the view controller, which might not even be necessary. Someone with more SwiftUI experience might be able to offer suggestions for improvement.
import Foundation
import SwiftUI
import UIKit
struct ActivityViewController: UIViewControllerRepresentable {
#Binding var shareURL: URL?
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> some UIViewController {
let containerViewController = UIViewController()
return containerViewController
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
guard let shareURL = shareURL, context.coordinator.presented == false else { return }
context.coordinator.presented = true
let activityViewController = UIActivityViewController(activityItems: [shareURL], applicationActivities: nil)
activityViewController.completionWithItemsHandler = { activity, completed, returnedItems, activityError in
self.shareURL = nil
context.coordinator.presented = false
if completed {
// ...
} else {
// ...
}
}
// Executing this asynchronously might not be necessary but some of my tests
// failed because the view wasn't yet in the view hierarchy on the first pass of updateUIViewController
//
// There might be a better way to test for that condition in the guard statement and execute this
// synchronously if we can be be sure updateUIViewController is invoked at least once after the view is added
DispatchQueue.main.asyncAfter(deadline: .now()) {
uiViewController.present(activityViewController, animated: true)
}
}
class Coordinator: NSObject {
let parent: ActivityViewController
var presented: Bool = false
init(_ parent: ActivityViewController) {
self.parent = parent
}
}
}
struct ContentView: View {
#State var shareURL: URL? = nil
var body: some View {
ZStack {
Button(action: { shareURL = URL(string: "https://apple.com") }) {
Text("Share")
.foregroundColor(.white)
.padding()
}
.background(Color.blue)
if shareURL != nil {
ActivityViewController(shareURL: $shareURL)
}
}
.frame(width: 375, height: 812)
}
}
iOS 15 / Swift 5 / Xcode 13
Extension to get the top presented UIViewController:
import UIKit
extension UIApplication {
// MARK: No shame!
static func TopPresentedViewController() -> UIViewController? {
guard let rootViewController = UIApplication.shared
.connectedScenes.lazy
.compactMap({ $0.activationState == .foregroundActive ? ($0 as? UIWindowScene) : nil })
.first(where: { $0.keyWindow != nil })?
.keyWindow?
.rootViewController
else {
return nil
}
var topController = rootViewController
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
return topController
}
}
Then use it to present your UIActivityViewController:
UIApplication.TopPresentedViewController?.present(activityViewController, animated: true, completion: nil)
Original Answer (deprecated code):
It's not pretty but you can call it directly like this (considering your app has only 1 window):
UIApplication.shared.windows.first?.rootViewController?.present(activityViewController, animated: true, completion: nil)
And if you get some warning blablabla:
Warning: Attempt to present ... which is already presenting ...
you can do something like this to get the top most view controller and call present on it.
There's a UIModalPresentationStyle which can be used to display certain presentations:
case pageSheet
A presentation style that partially covers the underlying content.
The way you apply the presentation style:
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
let v = UIActivityViewController(activityItems: sharing, applicationActivities: nil)
v.modalPresentationStyle = .pageSheet
return v
}
A list of the Presentations can be found here:
https://developer.apple.com/documentation/uikit/uimodalpresentationstyle
I haven't yet tested them all myself so I apologise in advance if this didn't end up working like you expected it to.
Alternatively you can have a look at this answer where they mention a third-party library, which will allow you to create a half modal in the way that it's usually presented.

Global alert in SwiftUI

I'm trying to present a global alert in SwiftUI. This alert should be displayed on top of everything regardless of what it is currently displayed / presented on screen (a sheet for example).
This is my code:
#main
struct MyApp: App {
#State private var showAlert = false
var body: some Scene {
WindowGroup {
MainView()
.onReceive(NotificationCenter.default.publisher(for:NSNotification.Name.SomeNotification), perform: { _ in
showAlert = true
})
.alert(
isPresented: $showAlert,
content: {Alert(title: Text("Alert!"))}
)
}
}
}
This in some cases will not work, for example if the notification is received when a sheet is currently presented on screen. In this case the alert is not displayed and the following message is displayed on the console:
Blockquote
[Presentation] Attempt to present <SwiftUI.PlatformAlertController: 0x7fbee6921400> on <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier_: 0x7fbee642ac60> (from <TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier_: 0x7fbee642ac60>) which is already presenting <TtGC7SwiftUI22SheetHostingControllerVS_7AnyView: 0x7fbee8405360>.
This make sense because I'm trying to present an alert on a view that is already presenting a sheet.
On UIKit I achieved this using the following class:
class GlobalAlertController: UIAlertController {
var globalPresentationWindow: UIWindow?
func presentGlobally(animated: Bool, completion: (() -> Void)?) {
globalPresentationWindow = UIWindow(frame: UIScreen.main.bounds)
globalPresentationWindow?.rootViewController = UIViewController()
globalPresentationWindow?.windowLevel = UIWindow.Level.alert + 1
globalPresentationWindow?.backgroundColor = .clear
globalPresentationWindow?.makeKeyAndVisible()
globalPresentationWindow?.rootViewController?.present(self, animated: animated, completion: completion)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
globalPresentationWindow?.isHidden = true
globalPresentationWindow = nil
}
}
This class allows me to display a global alert on top of everything in this way:
let alertController = GlobalAlertController(title: "Title", message: "Message", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Done", style: .cancel, handler: nil))
alertController.presentGlobally(animated: true, completion: nil)
Anyone know how to implement something like that in SwiftUI?
Just found that I can in fact use my old UIKit code to achieve this. The only thing that need to be changed is adding support for scenes (SwiftUI use scenes by design), like this:
class GlobalAlertController: UIAlertController {
var globalPresentationWindow: UIWindow?
func presentGlobally(animated: Bool, completion: (() -> Void)?) {
globalPresentationWindow = UIWindow(frame: UIScreen.main.bounds)
//This is needed when using scenes.
if let currentWindowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
globalPresentationWindow?.windowScene = currentWindowScene
}
globalPresentationWindow?.rootViewController = UIViewController()
globalPresentationWindow?.windowLevel = UIWindow.Level.alert + 1
globalPresentationWindow?.backgroundColor = .clear
globalPresentationWindow?.makeKeyAndVisible()
globalPresentationWindow?.rootViewController?.present(self, animated: animated, completion: completion)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
globalPresentationWindow?.isHidden = true
globalPresentationWindow = nil
}
}
Now I can just display the global alert like this:
#main
struct MyApp: App {
var body: some Scene {
WindowGroup {
MainView()
.onReceive(NotificationCenter.default.publisher(for:NSNotification.Name.SomeNotification), perform: { _ in
let alertController = GlobalAlertController(title: "Title", message: "Message", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Done", style: .cancel, handler: nil))
alertController.presentGlobally(animated: true, completion: nil)
})
}
}
}
It works, although a more SwiftUI like approach would be nice.

Swift 3.0 - Hide keyboard for multiple UItextfield

I have 5 UITextFields, everytime I clicked on the textfield, keyboard appeared. when user touch outside of the textfield, keyboard will hide. However, there is one special textField is for Pop Up. When Pop up appear, the previous textfield couldn't hide the keyboard. How am I gonna hide the keyboard first, and then show the pop up?
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == self.customerAddress{
scrollView.setContentOffset(CGPoint(x: 0,y:5), animated: true)
}
else if textField == self.district{
textField.resignFirstResponder()
scrollView.setContentOffset(CGPoint(x: 0,y:20), animated: true)
visualEffectView.isHidden = false
districtpicker.selectRow(3, inComponent: 0, animated: false)
self.view.addSubview(districtPopUp)
districtPopUp.center = self.subView.convert(CGPoint(x:subView.frame.size.width/2,y:subView.frame.size.height/3), to: subView)
districtPopUp.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
UIView.animate(withDuration: 0.4, animations: {
self.visualEffectView.alpha = 0.5
self.districtPopUp.alpha = 1
self.districtPopUp.transform = CGAffineTransform.identity
})
}
}
#IBAction func districtPopDismiss(_ sender: UIButton) {
scrollView.setContentOffset(CGPoint(x: 0,y:-64), animated: true)
UIView.animate(withDuration: 0.3, animations: {
self.districtPopUp.transform = CGAffineTransform.init(scaleX: 1.3, y: 1.3)
self.visualEffectView.alpha = 1
}) { (success) in
self.districtPopUp.removeFromSuperview()
}
self.visualEffectView.isHidden = true
}
func textFieldDidEndEditing(_ textField: UITextField) {
textField.resignFirstResponder()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
scrollView.setContentOffset(CGPoint(x: 0,y:-64), animated: true)
textField.resignFirstResponder()
return true
}
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(CustomerViewController.hideKeyboard))
subView.addGestureRecognizer(tapGesture)
visualEffectView.isHidden = true
self.customerName.delegate = self
self.customerAddress.delegate = self
self.customerContact.delegate = self
self.customerIC.delegate = self
self.ticketNumber.delegate = self
self.latitudeGPS.delegate = self
self.longitudeGPS.delegate = self
self.district.delegate = self
// Do any additional setup after loading the view.
}
func hideKeyboard(){
scrollView.setContentOffset(CGPoint(x: 0,y:-64), animated: true)
self.customerName.resignFirstResponder()
self.customerAddress.resignFirstResponder()
self.customerContact.resignFirstResponder()
self.customerIC.resignFirstResponder()
self.ticketNumber.resignFirstResponder()
self.latitudeGPS.resignFirstResponder()
self.longitudeGPS.resignFirstResponder()
self.district.resignFirstResponder()
}
Instead of invoking resignFirstResponder() on each of your textFields you can just invoke view.endEditing(true) and keyboard will hide. Try to invoke this before the logic responsible for presenting the popup.
Simple and easy for all view controller swift 3+
This code help you to hide keyboard on touch anywhere on viewcontrol
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
}
Use below code in your viewDidLoad
self.hideKeyboardWhenTappedAround()

UIGestureRecognizer in Swift Playgrounds

I am trying to integrate UIGestureRecognizer into my swift playground so that wherever I tap in my playground, my sprite goes to that point. I've tried everything! Watching countless youtube videos, and going on stack overflow for answers but everyone starts with making a class called view that is a UIView and in their code they keep referring to "self". I instead made a variable named "view" that was a SKView. I try to get parts of their code, put it in mine, and change it, but it just doesn't work. Here's the work that I got so far.
view.addGestureRecognizer(UIGestureRecognizer(target: view, action: #selector(handleTap(sender:))))
func handleTap(sender: UITapGestureRecognizer) {
player.position = sender.location(in: view)
}
My playground keeps telling me that i'm using an unresolved identifier 'handleTap(sender:)'
UIGestureRecognizer example with different states of Gesture recogniser in Playground
swift3+
import UIKit
import PlaygroundSupport
class ViewController : UIViewController {
var yellowView: UIView!
var redView: UIView!
var yellowViewOrigin: CGPoint!
override func loadView() {
// UI
let view = UIView()
view.backgroundColor = .white
yellowView = UIView()
yellowView.backgroundColor = .yellow
view.addSubview(yellowView)
redView = UIView()
redView.backgroundColor = .red
view.addSubview(redView)
// Layout
redView.translatesAutoresizingMaskIntoConstraints = false
yellowView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
yellowView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20.0),
yellowView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0),
yellowView.heightAnchor.constraint(equalToConstant: 80.0),
yellowView.widthAnchor.constraint(equalToConstant: 80.0),
redView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20.0),
redView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20.0),
redView.heightAnchor.constraint(equalToConstant: 80.0),
redView.widthAnchor.constraint(equalToConstant: 80.0)
])
self.view = view
}
override func viewDidLoad() {
super.viewDidLoad()
let pan = UIPanGestureRecognizer(target: self, action: #selector(self.handlePanGesture(_:)))
yellowView.addGestureRecognizer(pan)
yellowViewOrigin = yellowView.frame.origin
view.bringSubview(toFront: yellowView)
}
#objc func handlePanGesture(_ sender: UIPanGestureRecognizer) {
let targetView = sender.view!
let translation = sender.translation(in: view)
switch sender.state {
case .began,.changed:
targetView.center = CGPoint(x: targetView.center.x + translation.x
,y: targetView.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: view)
case .ended:
if targetView.frame.intersects(redView.frame){
UIView.animate(withDuration: 0.3) {
targetView.alpha = 0.0
}
}
else{
UIView.animate(withDuration: 0.3) {
targetView.frame.origin = self.yellowViewOrigin
}
}
break
default:
break
}
}
}
PlaygroundPage.current.liveView = ViewController()
I think you need to add the #objc mark in front of the function declaration, so that handleTap will be visible to #selector.
#objc func handleTap(sender: UITapGestureRecognizer) {
player.position = sender.location(in: view)
}
func handleTap has to be called from your view type.
Please try to add it inside an SKView extension:
extension SKView {
func handleTap(sender: UITapGestureRecognizer) {
player.position = sender.location(in: view)
}
}

Swift Hides back button

I want to hide the back button and set a title.
I'm using the following code:
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Einstellungen"
navigationItem.hidesBackButton = true }
But the title isn't shown and the back button is still there but if I touch it nothing happens. Can anybody help me please?
I found a solution on my own.
If I'm setting the title and the hidesBackButton from my previous ViewController everything works fine.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationVC = segue.destination as? ViewControllerFirstSettings {
destinationVC.navigationItem.hidesBackButton = true
destinationVC.navigationItem.title = "Einstellungen"
}
}
This code may help :
// MARK: - CUSTOM METHODS
func createNavBar() {
let leftNavigationButton = UIButton()
leftNavigationButton.setImage(UIImage(named: "ic_back.png"), forState: .Normal)
leftNavigationButton.frame = CGRectMake(10, 10, 20, 15)
leftNavigationButton.addTarget(self, action: "onBackButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
let customBarItem = UIBarButtonItem(customView: leftNavigationButton)
self.navigationItem.leftBarButtonItem = customBarItem;
//set TitleAppIcon
let GR = UITapGestureRecognizer(target: self, action: Selector("headerLogoTapAction:"))
let imageView = UIImageView(frame: CGRect(x: 90, y: 0, width: ((UIScreen.mainScreen().bounds.width)/3), height: 40))
imageView.addGestureRecognizer(GR)
imageView.userInteractionEnabled = true
navigationItem.titleView = imageView
}