Swift, scrollview and textfield auto scroll when the keyboard appears - swift

I have input form in popover.
When keyboard appears it shrinks in half..I have added in scroll view in base too but didn’t work..
Initializing:
var popoverViewController = self.storyboard?.instantiateViewControllerWithIdentifier("UnitEditController") as! UnitEditController
popoverViewController.modalPresentationStyle = .Popover
popoverViewController.preferredContentSize = CGSizeMake(820, 820)
popoverViewController.unit = unit
popoverViewController.property = property
popoverViewController.unitDetailProtocolVar = self
let popoverPresentationViewController = popoverViewController.popoverPresentationController
var rect = CGRectMake(cell!.bounds.origin.x+500, cell!.bounds.origin.y+20, 50, 30);
popoverPresentationViewController?.delegate = self
popoverPresentationViewController?.sourceView = cell!.contentView
popoverPresentationViewController?.sourceRect = cell!.frame
popoverPresentationViewController?.permittedArrowDirections = UIPopoverArrowDirection.allZeros
popoverPresentationViewController?.sourceRect = rect
presentViewController(popoverViewController, animated: true, completion: nil)
Prepare code for textfield and textview editing:
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
let scrollSize = CGSizeMake(900, 820)
self.contentScrollView.contentSize = scrollSize
func keyboardWillShow(sender: NSNotification) {
self.contentScrollView.frame=CGRectMake(0, -300, 320, 700);
}
func keyboardWillHide(sender: NSNotification) {
self.view.frame=CGRectMake(0, +300, 320, 700);
}
it should auto scroll accordingly when i start editing textview and textfield.
Can someone help me on this

this is what I did:
I created a keyboardHandler class
class KeyboardUtils{
static var instance = KeyboardUtils();
static var lastKeyboardSize : CGSize = CGSize();
var keyBoarEventsCallBacks : [(onShow: ()->Void,onHide: ()->Void)] = [];
init(){
self._registerKeyboardHandler()
KeyboardUtils.instance = self;
}
private func _registerKeyboardHandler(){
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
}
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0);
KeyboardUtils.lastKeyboardSize = keyboardSize.size;
for i in self.keyBoarEventsCallBacks{
i.onShow();
}
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
for i in self.keyBoarEventsCallBacks{
i.onHide();
}
}
}
}
this class will handle event when keyboard is opened.
now from my scrollView class i did this:
class myScrollView : UIScrollView,UIScrollViewDelegate{
override init(frame: CGRect)
{
super.init(frame:frame)
KeyboardUtils.instance.keyBoarEventsCallBacks.append((onShow:self.onKeyboardAppear,onHide: self.onKeyboardHide));
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func onKeyboardAppear(){
//I just resized, you can also do super.contentOffset =...
var size = super.contentSize;
size.height+=KeyboardUtils.lastKeyboardSize.height;
super.contentSize = size;
}
func onKeyboardHide(){
var size = super.contentSize;
size.height-=KeyboardUtils.height;
super.contentSize = size;
}
}
and now every time the keyboard will open, you'll have callback function to your UIViewScroll class. +it'll know the keyboard size that opened, because keyboard sizes might be different from phone to phone
edit:
you don't have to use custom view class, you can register any class
class myClass : anyClass{
override init(...)
{
super.init(...)
//on ViewController class do it on ViewDidLoad
KeyboardUtils.instance.keyBoarEventsCallBacks.append((onShow:self.onKeyboardAppear,onHide: self.onKeyboardHide));
}
func onKeyboardAppear(){
let keyboardSize =KeyboardUtils.lastKeyboardSize.height;
//do your stuff
}
func onKeyboardHide(){
let keyboardSize =KeyboardUtils.lastKeyboardSize.height;
//do your stuff
}
}

Related

End or Dismiss Lottie Animation View if playOnce is finished in Swift

I already tried both loadingview.removeFromSuperView and loadingView.isHidden = true
Yes, it removes or hides the view, but I can't click on my root view anymore.
I also tried animatonview.background = .forceFinish, but doesn't do the job.
import UIKit
import Lottie
class LoadingAnimationView: UIView {
#IBOutlet weak var loadingView: UIView!
let animationView = AnimationView()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
}
func loadAnimation() {
let animation = Animation.named("success")
animationView.animation = animation
animationView.contentMode = .scaleAspectFill
loadingView.addSubview(animationView)
animationView.backgroundBehavior = .pauseAndRestore
animationView.translatesAutoresizingMaskIntoConstraints = false
animationView.topAnchor.constraint(equalTo: loadingView.layoutMarginsGuide.topAnchor).isActive = true
animationView.leadingAnchor.constraint(equalTo: loadingView.leadingAnchor, constant: 0).isActive = true
animationView.bottomAnchor.constraint(equalTo: loadingView.bottomAnchor).isActive = true
animationView.trailingAnchor.constraint(equalTo: loadingView.trailingAnchor, constant:0).isActive = true
animationView.setContentCompressionResistancePriority(.fittingSizeLevel, for: .horizontal)
animationView.play(fromProgress: 0,
toProgress: 1,
loopMode: .playOnce,
completion: { (finished) in
if finished {
print("Animation Complete")
//please put solution here? dismiss or end loadingView or animationView
} else {
print("Animation cancelled")
}
})
}
EDIT 2:
I'm using the loadingView when the success message is received or 200.
func goOnlineMode(){
APIManager.sharedInstance.fetchServerStatus(completion: { data, error in
if error != nil{
print("Connection Failed")
} else {
if data?.status == 200 || data?.msg == "success" {
print("connected")
loadAnimation(true)
self.setCloudStateValue(value: true)
self.vc.cloudstateChecker()
} else {
print("fail to connect")
}
}
})
}
}
this is my function loading boolean in loadAnimation for Loading the xib.
func loadAnimation(_ display: Bool) {
if (display) {
let window = UIApplication.shared.keyWindow!
if Singleton.animationView == nil {
if let view = Bundle.main.loadNibNamed("LoadingAnimationView", owner: window, options:nil)![0] as? LoadingAnimationView {
Singleton.animationView = view
Singleton.animationView?.frame.size = CGSize(width: window.bounds.width, height: window.bounds.height)
window.addSubview(Singleton.animationView!)
window.layoutIfNeeded()
Singleton.animationView?.loadAnimation()
Singleton.animationView?.translatesAutoresizingMaskIntoConstraints = false
Singleton.animationView?.leftAnchor.constraint(equalTo: window.leftAnchor).isActive = true
Singleton.animationView?.rightAnchor.constraint(equalTo: window.rightAnchor).isActive = true
Singleton.animationView?.topAnchor.constraint(equalTo: window.topAnchor, constant:-60).isActive = true
Singleton.animationView?.bottomAnchor.constraint(equalTo: window.bottomAnchor).isActive = true
window.layoutIfNeeded()
}
}
} else {
if (Singleton.animationView != nil) {
Singleton.animationView?.removeFromSuperview()
Singleton.animationView = nil
}
}
}
Try with this:
Swift 5
animationView.play { (finished) in
animationViewNewOrder!.isHidden = true
}
I solved my problem by using NotificationCenter
Swift 4.2
Add this NotificationCenter Observer in your MainViewController, and also register a Notification.Name to your Constants
NotificationCenter.default.addObserver(self, selector: #selector(removeAnimation(notification:)), name: HIDE_ANIMATION, object: nil)
}
also add this together with your observer
#objc func removeAnimation(notification:NSNotification) {
loadingAnimation(false)
}
I put this Notification Post in my newly created hideAnimation function in LoadingAnimationView.
func hideAnimation() {
NotificationCenter.default.post(name: Notification.Name(HIDE_ANIMATION.rawValue), object: nil)
loadingView.removeFromSuperview()
}
and put the hideAnimation function to your completion finish.

space between textView and keyboard

I am facing an issue, whenever I going to type in ALTextInputBar() there is a space between keyboard and ALTextInputBar() of 44 points. I don't know from where it is coming. Please have a look on code and image.
#IBOutlet weak var viewChatBox: UIView!
#IBOutlet weak var viewChatBoxBottomConstraint: NSLayoutConstraint!
let textInputBar = ALTextInputBar()
let keyboardObserver = ALKeyboardObservingView()
override func viewDidLoad() {
super.viewDidLoad()
IQKeyboardManager.shared.enable = false
IQKeyboardManager.shared.enableAutoToolbar = false
configureView()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if #available(iOS 11, *) {
// safe area constraints already set
}
else {
if (!(self.topLayoutConstraint != nil)) {
topLayoutConstraint = viewTopBar.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor)
self.topLayoutConstraint?.isActive = true
}
if (!(self.bottomLayoutConstraint != nil)) {
// bottomLayoutConstraint = viewChatBox.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor)
self.bottomLayoutConstraint?.isActive = true
}
}
}
func configureInputBar () {
btnChat = UIButton(frame: CGRect(x: 0, y: 0, width: 36, height: 36))
keyboardObserver.isUserInteractionEnabled = false
textInputBar.showTextViewBorder = true
textInputBar.leftView = nil
textInputBar.rightView = btnChat
textInputBar.alwaysShowRightButton = true
textInputBar.delegate = self
textInputBar.textView.autocorrectionType = .yes
textInputBar.backgroundColor = UIColor(white: 0.95, alpha: 1)
textInputBar.keyboardObserver = keyboardObserver
viewChatBox.addSubview(textInputBar)
applyConstraintToChatBox()
self.view.layoutIfNeeded()
}
func applyConstraintToChatBox() {
textInputBar.translatesAutoresizingMaskIntoConstraints = false
viewChatBox.translatesAutoresizingMaskIntoConstraints = false
let views = ["textView": textInputBar]
let hConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[textView]-0-|", options: [], metrics: nil, views: views)
let vConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[textView]-0-|", options: [], metrics: nil, views: views)
viewChatBox.addConstraints(hConstraints)
viewChatBox.addConstraints(vConstraints)
}
override var inputAccessoryView: UIView? {
get {
return keyboardObserver
}
}
override var canBecomeFirstResponder: Bool {
return true
}
//MARK: - TEXTVIEW DELEGATE
func textView(textView: ALTextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if (textInputBar.text.count == 0) {
return true
}
let newLength = textInputBar.text.count + text.count - range.length
return (newLength <= 144);
}
func inputBarDidChangeHeight(height: CGFloat) {
UIView.animate(withDuration: 0.3, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: [.curveLinear], animations: { () -> Void in
self.view.layoutIfNeeded()
}, completion: nil)
}
// MARK: - KEYBOARDS
#objc func keyboardWillChangeFrame(notification: Notification) {
let endFrame = ((notification as NSNotification).userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
viewChatBoxBottomConstraint.constant = view.bounds.height - endFrame.origin.y
print("CHAT BOX FRAME: \(viewChatBox.frame)")
print("TEXT FRAME FRAME: \(textInputBar.frame)")
self.view.layoutIfNeeded()
}
Late answer but may help someone. I had the same issue and found this below code in the documentation.
Only helpful for IQKeyboardManager users.
IQKeyboardManager
self.textField.keyboardDistanceFromTextField = 8; //This will modify default distance between textField and keyboard. For exact value, please manually check how far your textField from the bottom of the page. Mine was 8pt.

How do I show activity indicator for ASNetworkImageNode?

Here is the code that I tried, but when the image has not loaded yet, nothing show up (an animated indicator should show up). What is the best practice of showing the activity indicator? I hooked into the ASNetworkImageNodeDelegate.
import AsyncDisplayKit
class WideImageFeedNode : ASCellNode, ASNetworkImageNodeDelegate {
var imageNode = ASNetworkImageNode()
var activityIndicator:UIActivityIndicatorView?
init(itemid:Int) {
super.init()
imageNode.backgroundColor = ASDisplayNodeDefaultPlaceholderColor()
let imgURL = URL(string:"http://...somelargeimage.jpg")
imageNode.url = imgURL
imageNode.delegate = self
self.addSubnode(imageNode)
self.automaticallyManagesSubnodes = true
}
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
var finalStackArr:[ASLayoutElement] = [self.imageNode]
let finalSpec = ASStackLayoutSpec(direction: .vertical, spacing: 10.0, justifyContent: .start, alignItems: .start, children: finalStackArr)
return finalSpec
}
func imageNode(_ imageNode: ASNetworkImageNode, didLoad image: UIImage) {
if let activityIndicator = self.activityIndicator {
activityIndicator.removeFromSuperview()
self.activityIndicator = nil
}
self.setNeedsLayout()
}
// helper functions
func setupActivityIndicator(bounds:CGSize) -> UIActivityIndicatorView {
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
var refreshRect = activityIndicator.frame
refreshRect.origin = CGPoint(x: (bounds.width - activityIndicator.frame.size.width) / 2.0, y: (bounds.height - activityIndicator.frame.size.height) / 2.0)
activityIndicator.frame = refreshRect
return activityIndicator
}
func imageNodeDidStartFetchingData(_ imageNode: ASNetworkImageNode) {
self.activityIndicator = setupActivityIndicator(bounds: imageNode.style.preferredSize)
imageNode.view.addSubview(self.activityIndicator!)
}
func imageNode(_ imageNode: ASNetworkImageNode, didFailWithError error: Error) {
if let activityIndicator = self.activityIndicator {
activityIndicator.removeFromSuperview()
self.activityIndicator = nil
}
}
}
Never mind, it works. I was missing one call:
func imageNodeDidStartFetchingData(_ imageNode: ASNetworkImageNode) {
self.activityIndicator = setupActivityIndicator(bounds: imageNode.style.preferredSize)
imageNode.view.addSubview(self.activityIndicator!)
self.activityIndicator!.startAnimating()
}

Find out when UIKeyboard.frame intersects with other frame?

I need to find out when the textfield becomes the first responder to notify me whether the keyboard that's going to show will obstruct the UITextField. If it does, I wanna adjust the scrollview properties.
So far I have this setup. I'm listening for UIKeyboardWillShow notifications that calls the following selector:
func keyboardWillAppear(notification:NSNotification)
{
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
{
if keyboardSize.intersects(textField.frame)
{
print("It intersects")
}
else
{
print("Houston, we have a problem")
}
}
Note: I tried with UIKeyboardDidShow but still no success. UITextField is a subview of the scrollView.
listen to size changes of the keyboard
CONVERT the coordinates
working sample:
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
//keyboard observers
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
func keyboardWillChange(notification:NSNotification)
{
print("Keyboard size changed")
if let keyboardSize = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? CGRect {
//convert gotten rect
let r = self.view.convert(keyboardSize, from: nil)
//test it
if r.intersects(textView.frame) {
print("intersects!!!")
}
}
}
How about comparing the start position of the keyboard with the end position of the text?
working sample:
func keyboardWillAppear(notification:NSNotification)
{
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
{
if keyboardSize.origin.y < textField.frame.origin.y + textField.frame.size.height {
print("It intersects")
} else {
print("Houston, we have a problem")
}
}
}

Get the frame of the keyboard dynamically

Is it possible to get the frame, actually its height, of the keyboard dynamically? As I have a UITextView and I would like to adjust its height according to the keyboard frame height, when the input method of the keyboard is changed. As you know, different input methods may have different keyboard frame height.
try this:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification
object:nil];
- (void)keyboardWasShown:(NSNotification *)notification
{
// Get the size of the keyboard.
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
//Given size may not account for screen rotation
int height = MIN(keyboardSize.height,keyboardSize.width);
int width = MAX(keyboardSize.height,keyboardSize.width);
//your other code here..........
}
Tutorial for more information
Just follow this tutorial from Apple and you will get what you want. Apple Documentation. In order to determine the area covered by keyboard please refer to this tutorial.
For the Swift 3 users, the #Hector code (with some additions) would be:
In your viewDidLoad add the observer :
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidShow(_:)), name: .UIKeyboardDidShow , object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardDidHide(_:)), name: .UIKeyboardDidHide , object: nil)
Then implement those methods:
func keyboardDidShow(_ notification: NSNotification) {
print("Keyboard will show!")
// print(notification.userInfo)
let keyboardSize:CGSize = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue.size
print("Keyboard size: \(keyboardSize)")
let height = min(keyboardSize.height, keyboardSize.width)
let width = max(keyboardSize.height, keyboardSize.width)
}
func keyboardDidHide(_ notification: NSNotification) {
print("Keyboard will hide!")
}
You can add this code to the view which contains the text field in Swift 3. This will make the text field animate up and down with the keyboard.
private var keyboardIsVisible = false
private var keyboardHeight: CGFloat = 0.0
// MARK: Notifications
private func registerForKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
private func deregisterFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
// MARK: Triggered Functions
#objc private func keyboardWillShow(notification: NSNotification) {
keyboardIsVisible = true
guard let userInfo = notification.userInfo else {
return
}
if let keyboardHeight = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.height {
self.keyboardHeight = keyboardHeight
}
if !textField.isHidden {
if let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber,
let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber {
animateHUDWith(duration: duration.doubleValue,
curve: UIViewAnimationCurve(rawValue: curve.intValue) ?? UIViewAnimationCurve.easeInOut,
toLocation: calculateTextFieldCenter())
}
}
}
#objc private func keyboardWillBeHidden(notification: NSNotification) {
keyboardIsVisible = false
if !self.isHidden {
guard let userInfo = notification.userInfo else {
return
}
if let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber,
let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber {
animateHUDWith(duration: duration.doubleValue,
curve: UIViewAnimationCurve(rawValue: curve.intValue) ?? UIViewAnimationCurve.easeInOut,
toLocation: calculateTextFieldCenter())
}
}
}
// MARK: - Helpers
private func animateHUDWith(duration: Double, curve: UIViewAnimationCurve, toLocation location: CGPoint) {
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(TimeInterval(duration))
UIView.setAnimationCurve(curve)
textField.center = location
UIView.commitAnimations()
}
private func calculateTextFieldCenter() -> CGPoint {
if !keyboardIsVisible {
return self.center
} else {
let yLocation = (self.view.frame.height - keyboardHeight) / 2
return CGPoint(x: self.center.x, y: yLocation)
}
}