I've created a custom inputView for the standard iOS keyboard and it works perfectly fine in both iOS 7 and 8.
But when using 3rd-party custom keyboard, it covers my created inputView. It shows the custom inputView for a second, and covers it with the custom keyboard view. I've tried several different custom keyboards, and they all have the same problem.
Is it possible to use inputView on any kind of keyboards? If possible, how to do it?
Thanks in advice!
I met the same problem and I found that using a view that is managed by a view controller will lead to this issue.
I made a simple demo to demostrate it:
As code shown below, toggleInputView will create a input view that work for both system keyboard and third party keyboard, while toggleInputViewController will only work for system keyboard.
import UIKit
class CustomInputViewController: UIViewController {
override func viewDidLoad() {
self.view.frame = CGRect(x: 0, y: 0, width: 300, height: 200)
}
}
class ViewController: UIViewController {
var customInputViewController: CustomInputViewController?
#IBOutlet weak var textView: UITextView!
#IBAction func toggleInputView(sender: AnyObject) {
if textView.inputView == nil {
textView.inputView = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 200))
} else {
textView.inputView = nil
}
self.textView.reloadInputViews()
}
#IBAction func toggleInputViewController(sender: AnyObject) {
if textView.inputView == nil {
if self.customInputViewController == nil {
self.customInputViewController = CustomInputViewController()
}
self.textView.inputView = self.customInputViewController!.view
} else {
self.textView.inputView = nil
}
self.textView.reloadInputViews()
}
}
As a result, you can build a input view by subclass UIView to resolve your problem.
Related
I recently added some keyboard shortcuts to the app that I work on after reading this article: https://www.swiftbysundell.com/tips/handling-keyup-and-keydown-events/
One of the shortcuts was using the spacebar to toggle play/pause of the currently loaded media, and it worked as expected. You could hit the spacebar on an external keyboard from anywhere in the app and it would play/pause. However I found that when I had an active UITextField or UITextView, the user couldn't type spaces from an external keyboard, and if they tried it would still toggle play/pause. My understanding of the responder chain is that the when a UITextField is the first responder, it should receive those events first. But it doesn't in this case.
Example Code
I verified this behavior by creating a blank app and adding the following code:
import UIKit
class ViewController: UIViewController {
private var isActive: Bool = false
private var textField = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemRed
textField.backgroundColor = .systemBackground
view.addSubview(textField)
}
override func viewDidLayoutSubviews() {
textField.frame = CGRect(x: 20, y: view.safeAreaInsets.top + 20, width: view.frame.width - 40, height: 30)
}
override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
if presses.first?.key?.keyCode == .keyboardSpacebar {
isActive.toggle()
view.backgroundColor = isActive ? .systemBlue : .systemRed
} else {
super.pressesBegan(presses, with: event)
}
}
}
If you run this you'll notice that the textfield will receive events for everything you type except the spacebar. That indicates to me that this UIViewController is receiving those events before the UITextField.
Is this expected behavior? Is my understanding of the responder chain wrong? Or is this a bug in UIKit?
Is it possible to put a loading animation over the VNDocumentViewController? As in, when the user presses the Save button, is there a way for me to somehow indicate that the Vision is processing the image and hasn't frozen? Right now, in my app, there is a long pause between the user pressing Save and the actual image being processed.Here is an example from another post of what I'm trying to create
Here is one example of adding a loading indicator using UIActivityIndicatorView().
startAnimating() to start the animation and stopAnimation() to stop the animation.
iOS - Display a progress indicator at the center of the screen rather than the view
guard let topWindow = UIApplication.shared.windows.last else {return}
let overlayView = UIView(frame: topWindow.bounds)
overlayView.backgroundColor = UIColor.clear
topWindow.addSubview(overlayView)
let hudView = UIActivityIndicatorView()
hudView.bounds = CGRect(x: 0, y: 0, width: 20, height: 20)
overlayView.addSubview(hudView)
hudView.center = overlayView.center
hudView.startAnimating()
Alternatively, you could look into using Cocoapod MBProgressHud
https://cocoapods.org/pods/MBProgressHUD
There's a way you can extend a class in Swift that captures this problem well. The idea is you want a UIActivityIndicator in your VNDocumentCameraViewController. But we'd like that to be a part of every version of this we use. We could simply embed the DocumentVC's view into our current view and superimpose a UIActivityIndicator above it in the view stack, but that's pretty hacky. Here's a quick way we can extend any class and solve this problem
import VisionKit
import UIKit
extension VNDocumentCameraViewController {
private struct LoadingContainer {
static var loadingIndicator = UIActivityIndicatorView()
}
var loadingIndicator: UIActivityIndicatorView {
return LoadingContainer.loadingIndicator
}
func animateLoadingIndicator() {
if loadingIndicator.superview == nil {
view.addSubview(loadingIndicator)
//Setup your constraints through your favorite method
//This constrains it to the very center of the controller
loadingIndicator.frame = CGRect(
x: view.frame.width / 2.0,
y: view.frame.height / 2.0,
width: 20,
height: 20)
//Setup additional state like color/etc here
loadingIndicator.color = .white
}
loadingIndicator.startAnimating()
}
func stopAnimatingLoadingIndicator() {
loadingIndicator.stopAnimating()
}
}
The place we can call these functions are in the delegate methods for VNDocumentCameraViewController that you implement in your presenting ViewController:
func documentCameraViewController(
_ controller: VNDocumentCameraViewController,
didFinishWith scan: VNDocumentCameraScan
) {
controller.animateLoadingIndicator()
}
there is a requirement like add an imageview to all viewcontrollers but I have 150+ xib's and it is time consuming to put imageview in every single xib.
Is there a common way to do it? I googled but nothing useful found.
Any help would be appreciated.
It will be easy if you use base class like this
class BaseVC: UIViewController
{
var imageView:UIImageView = UIImageView.init()
override func viewDidLoad() {
super.viewDidLoad()
addImageView()
}
override func viewWillAppear(_ animated: Bool) {
//self.view.bringSubview(toFront: imageView) //To bring imageview infront of other views put this method as per your requirement
}
func addImageView(name:String = "default")
{
let image = UIImage(named: name)
imageView.image = image
imageView.frame = CGRect(x: 0, y: 0, width: 100, height: 200)
//view.addSubview(imageView)
view.insertSubview(imageView, at: 0) /*For put image view below all image*/
}
}
You need to derive all your view controller from this like this
class YourVC: BaseVC
also you can change the image with different viewcontrollers.
like
class YourVC: BaseVC
{
override func viewDidLoad() {
super.viewDidLoad()
addImageView(name:"xyz")
}
}
you can write an extension for UIView to add imageview into it and run it into every your viewcontrollers.
extension UIView {
func addImage() {
let imageView = UIImageView(frame: frame)
imageView.image = UIImage(named: "Your Image Name")
addSubview(imageView)
imageView.didMoveToSuperview()
}
}
Create New ViewController without storyboard.
import UIKit
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setImageView()
}
func setImageView() {
let thisImageView = UIImageView(frame: CGRect(x: 10, y: 10, width: 70, height: 70))
thisImageView.image = UIImage(named: "your image name")
view.addSubview(thisImageView)
}
}
now you can set this BaseViewController as delegate of your project's ViewControllers.
import UIKit
class MainViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
now you have this ImageView in MainViewController and all ViewControllers have BaseViewContoller as delegate.
Hope to be useful. Also sorry about my English.
TL;DR No. Based on my understanding of what you are imagining, no you can't write a function to add a UIImageView to every one of your viewControllers.
Long answer:
You need to create a separate controller swift file for each View and set it up in that file. You could create a supporting file in which you setup up the the ImageView then call in in the viewDidLoad for each view controller. (You could even make this an extension of UIView so you can just call something like self.setUpImageView())
My personal recommendation would be to drop the xibs as soon as you can and recreate everything pragmatically, I know it's a headache to throw all your work away but it is really worth it in the end on top of just being good practice. I have a file that I found that makes autolayout a breeze that I can share with you if you'd like. I used to really enjoy storyboards and xibs myself but they are a hassle that just isn't worth it anymore and cause such a headache in situation like this.
This question already has answers here:
How to display UIView over keyboard in iOS
(7 answers)
Closed 5 years ago.
I need to present a help screen that overlays an open keyboard - the help screen should dim the whole view underneath and keep just a small hole with full transparency to "highlight" that piece. The point is to provide some information about several view components while highlighting them. Without a keyboard, I could just put a view at top of the hierarchy, but in this case the UI uses a keyboard with a custom input accessory that needs to be visible.
I tried to insert a new UIWindow and put it above all the UIWindows:
class CustomTextField: UITextField {
override var canResignFirstResponder: Bool {
return false
}
}
class ViewController: UIViewController {
var textField: UITextField = CustomTextField()
override func viewDidAppear(_ animated: Bool) {
view.backgroundColor = .white
super.viewDidAppear(animated)
textField.frame = CGRect(x: 0, y: 0, width: 200, height: 50)
view.addSubview(textField)
textField.backgroundColor = UIColor.gray
textField.becomeFirstResponder()
DispatchQueue.main.asyncAfter(wallDeadline: .now() + 1) {
self.window.windowLevel = 100000002.0 // based on experiments with UIApplication.shared.windows this should be the top most window
let controller = UIViewController()
controller.view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
self.window.rootViewController = controller
self.window.makeKeyAndVisible()
}
}
let window = UIWindow(frame: UIScreen.main.bounds)
}
But there are two problems with this approach:
The keyboard gets hidden as soon as the window becomes key and visible.
Even when using windowLevel = 100000002.0 it seems that the keyboard is above the window (the keyboard gets animated, so while hiding, I can see that its above my window).
Any ideas how to deal with these two problems? Is it even possible?
OK, as pointed out by #Krunal, this is kind of a duplicate of this question. The trick there is to add the overlay view to the window in which keyboard is (which happens to be the UIApplication.shared.windows.last):
class ViewController: UIViewController {
var textField: UITextField = UITextField()
override func viewDidAppear(_ animated: Bool) {
view.backgroundColor = .white
super.viewDidAppear(animated)
textField.frame = CGRect(x: 0, y: 0, width: 200, height: 50)
view.addSubview(textField)
textField.backgroundColor = UIColor.gray
textField.becomeFirstResponder()
DispatchQueue.main.asyncAfter(wallDeadline: .now() + 1) {
// this does the trick
let customView = UIView(frame: self.view.bounds)
customView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
customView.layer.zPosition = CGFloat(Float.greatestFiniteMagnitude)
UIApplication.shared.windows.last?.addSubview(customView)
}
}
}
I am building Application in iOS swift 3.0. I am creating dynamic UIViews. I need to remove custom view randomly, But currently I am unable to relocate the positions as I get the gaps between the two views and I want remove them, as shows in the picture with code below, Kindly help me with this.
class ViewController: UIViewController {
var myView: subView!
var y : CGFloat!
var tag : Int = 0
#IBOutlet weak var addButton: UIButton!
override func viewDidLoad() {
y = 1
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func cancelbutton(_ sender: UIButton)
{
let selectViewTagValue : Int = sender.tag /// save the selected view tag value
for object in self.view.subviews {
if ((object is subView) && object.tag == selectViewTagValue)
{
object.removeFromSuperview()
}
}
}
#IBAction func buttonAction(_ sender: Any) {
y = y + 110
myView = subView(frame: CGRect(x: 80, y: y, width: 300, height: 100))
myView.tag = tag
myView.actionButton.tag = tag
tag = tag + 1
myView.backgroundColor = UIColor.green
myView.actionButton.addTarget(self, action: (#selector(cancelbutton(_:))), for: UIControlEvents.touchUpInside)
self.view.addSubview(myView)
}
Please Help me this issue... "Thanks in advance"
When you have multiple similar views to be displayed and re-arranged dynamically, UITableView is a smart choice. Re-arranging frames would be tedious to maintain and requires lot of code to implement. Why not use tools provided by UITableView? Just use deleteRowsAtIndexPaths to delete your view and the rest will be taken care for you.