Move View Behind Keyboard When textfield selected - iphone

Recently I used this to Move View Behind Keyboard When textfield is selected
http://www.ioscreator.com/tutorials/move-view-behind-keyboard-ios8-swift
What if I need to move view only for some inputs, others are near top and don't need it? I tries the following code but no success in that.
var searcher
func keyboardWillShow(notification: NSNotification) {
if (searcher.tag == 1){
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
kbHeight = keyboardSize.height
self.animateTextField(true)
}
}
}
}
Thanks in advance!

I would recommend you to use AutoKeyboardScrollView library instead of adding code manually. This library already does what you need with the expected behaviour.
As seen on the project's readme:

Another great library for this is IQKeyboardManager. https://github.com/hackiftekhar/IQKeyboardManager
Simple to use and will do this globally for your entire app. Easy configuration and it adds previous, next and done functionality
If you're using cocoapods, just add
pod 'IQKeyboardManager-Swift'
Check out the docs on the GitHub page for the config options

Related

(UIApplication.shared.keyWindow?.rootViewController as? BaseSlidingController)?.openMenu() return nil Swift 4.2

I have a problem with the above stated. I can not find the exact information on the forums. Most of them are outdated and I have written the code programmatically. I have a controller that contains a view to edit the profile. I can not access that after changing the function listed below. I have a rootviewcontroller set to something else, but I tried the UiApplication calls anyway and it return nil and I can not open the profile controller. This is the function listed below.
#objc func handleOpen2() {
(UIApplication.shared.keyWindow?.rootViewController as? BaseSlidingController)?.openMenu()
}
Xcode does not give me an error but I can not get my menu to open. My rootviewcontroller is set to something else in app delegate. I have a controller that is used to control the sliding menu when I press the edit profile button.
func openMenu() {
isMenuOpened = true
redViewLeadingConstraint.constant = menuWidth
redViewTrailingConstraint.constant = menuWidth
performAnimations()
setNeedsStatusBarAppearanceUpdate()
}
This code is used to open my side bar menu with the information I need and also to perform animations as well. I was wondering if someone had any idea what I can do different instead in my handleOpen2 function. If you need more code, please let me know. Thanks
On swift 5 version:
(UIApplication.shared.windows.first?.rootViewController as? BaseSlidingController)?.openMenu()

How to Remove windows from UIApplication in Swift

While clicking on the button , i am moving to another view controller using the following code.
var window: UIWindow?
window = UIWindow.init(frame: UIScreen.main.bounds)
window?.autoresizesSubviews = true
window?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
let trackingViewController = LoginCameraViewController.init(screen:
.main)
window?.rootViewController = trackingViewController
window?.addSubview((trackingViewController?.view)!)
window?.makeKeyAndVisible()
window?.layoutSubviews()
For every button click, a new window is added to the application.I want to remove the latest window added.
The number of windows present in the application can be known by using following code.
let windowz = UIApplication.shared.windows
print("subviews",windowz)
I think you get the wrong concept of navigation in iOS. Window is like a root object in which ViewControllers appear. So probably the solution you're looking in a first place is UINavigationController.
Apple Documentation on Navigation
For iOS 13 i was able to do it this way
I created array which contains the window using which this new viewController is being presented,
var arrWindow = [UIWindow]()
arrWindow.append(yourNewWindow)
// Note: This will be stored as strong reference so need to remove it.
Also store your original window in variable
let originalWindow = yourOriginalWindow
// Note: Same goes for this as well ,this will be stored as strong reference so need to remove it.
At the time of removing there are many ways to do it but this was the most suited way for me,
func removeAppendedWindow() {
for window in arrWindow {
if window != originalWindow {
if let index = arrWindow.index(of: window) {
window.isHidden = true
arrWindow.remove(at: index)
}
}
}
}
In the below code windowz is normal array.
let windowz = UIApplication.shared.windows
You can remove last by using
windowz.removeLast()
You should use View Controller instead of adding windows and pop it instead where you are removing the window.
Window is only one object for app and will contain the views.
Please correct your understanding and use View controllers.

Prefer Large Titles and RefreshControl not working well

I am using this tutorial to implement a pull-to-refresh behavior with the RefreshControl. I am using a Navigation Bar. When using normal titles everything works good. But, when using "Prefer big titles" it doesn't work correctly as you can see in the following videos. Anyone knows why? The only change between videos is the storyboard check on "Prefer Large Titles".
I'm having the same problem, and none of the other answers worked for me.
I realised that changing the table view top constraint from the safe area to the superview fixed that strange spinning bug.
Also, make sure the constant value for this constraint is 0 🤯.
At the end what worked for me was:
In order to fix the RefreshControl progress bar disappearing bug with large titles:
self.extendedLayoutIncludesOpaqueBars = true
In order to fix the list offset after refreshcontrol.endRefreshing():
let top = self.tableView.adjustedContentInset.top
let y = self.refreshControl!.frame.maxY + top
self.tableView.setContentOffset(CGPoint(x: 0, y: -y), animated:true)
If you were using tableView.tableHeaderView = refreshControl or tableView.addSubView(refreshControl) you should try using tableView.refreshControl = refreshControl
It seems there are a lot of different causes that could make this happen, for me I had a TableView embedded within a ViewController. I set the top layout guide of the tableview to the superview with 0. After all of that still nothing until I wrapped my RefreshControl end editing in a delayed block:
DispatchQueue.main.async {
if self.refreshControl.isRefreshing {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
self.refreshControl.endRefreshing()
})
}
}
The only working solution for me is combining Bruno's suggestion with this line of code:
tableView.contentInsetAdjustmentBehavior = .always
I've faced the same problem. Call refreshControl endRefreshing before calling further API.
refreshControl.addTarget(controller, action: #selector(refreshData(_:)), for: .valueChanged)
#objc func refreshData(_ refreshControl: UIRefreshControl) {
refreshControl.endRefreshing()
self.model.loadAPICall {
self.tableView.reloadData()
}
}
The only solution that worked for me using XIBs was Bruno's one:
https://stackoverflow.com/a/54629641/2178888
However I did not want to use a XIB. I struggled a lot trying to make this work by code using AutoLayout.
I finally found a solution that works:
override func loadView() {
super.loadView()
let tableView = UITableView()
//configure tableView
self.view = tableView
}
I had this issue too, and i fixed it by embedded my scrollView (or tableView \ collectionView) inside stackView, and it's important that this stackView's top constraint will not be attached to the safeArea view (all the other constraints can). the top constraint should be connect to it's superview or to other view.
I was facing the same issue for very long, the only working solution for me was adding refresh control to the background view of tableview.
tableView.backgroundView = refreshControl
Short Answer
I fixed this by delaying calling to API until my collection view ends decelerating
Long Answer
I notice that the issue happens when refresh control ends refreshing while the collection view is still moving up to its original position. Therefore, I delay making API call until my collection view stops moving a.k.a ends decelerating. Here's a step by step:
Follow Bruno's suggestion
If you set your navigation bar's translucent value to false (navigationBar.isTranslucent = false), then you will have to set extendedLayoutIncludesOpaqueBars = true on your view controller. Otherwise, skip this.
Delay api call. Since I'm using RxSwift, here's how I do it.
collectionView.rx.didEndDecelerating
.map { [unowned self] _ in self.refreshControl.isRefreshing }
.filter { $0 == true }
.subscribe(onNext: { _ in
// make api call
})
.disposed(by: disposeBag)
After API completes, call to
refreshControl.endRefreshing()
Caveat
Do note that since we delay API call, it means that this whole pull-to-refresh process is not as quick as it could have been done without the delay.
Unfortunately, no advice helped. But I found a solution that helped me. Setting the transparency of the navigation bar helped.enter image description here
Problem can be solved if add tableview or scroll view as root view in UIViewController hierarchy (like in UITableViewController)
override func loadView() {
view = customView
}
where customView is UITableView or UICollectionView

Customise UITabBarController.moreNavigationController UITableViewCells

As stated in the title, I'm trying to customise the cells in the moreNavigationController. I've read a number of SO posts asking a similar question but most are outdated and seem to be hacks.
I'm assuming this is a very common situation so there must be a 'tidy' way of doing this surely?
I've looked into overriding one of the tableviews delegate methods but I couldn't figure that out assuming it's even possible. I've also tried the following:
override func viewDidLoad() {
super.viewDidLoad()
let moreNavController = self.moreNavigationController
if let moreTableView = moreNavController.topViewController?.view as? UITableView {
for cell in moreTableView.visibleCells {
cell.textLabel?.textColor = AppDarkColor
cell.imageView?.image = cell.imageView?.image?.imageWithRenderingMode(.AlwaysTemplate)
}
}
}
But moreTableView.visibleCells is empty at least when the UITabBarController is loaded. Maybe I need to move this somewhere else but I'm not sure where or if this is the right approach.
Experimented putting the code in viewWillAppear. It works for me (at least). Hope it works for you!

NSTextView, NSFontManager, and changeAttributes()

So I have this app that I'm writing to get familiar with Swift and programming for OSX. It's a note-taking app. The note window consists of an NSTextView and a button that brings up an NSFontPanel.
Changing the font works great. Selecting a size? No problem. Want to change attributes of the font like color, underlining, etc? I'm not at all sure how to get this to work.
Other sources (here and here, for example) seem to suggest that NSTextView should be the target of NSFontManager and that NSTextView has it's own implementation of changeAttributes(). Making NSTextView the target, however, does nothing. When I select text in NSTextView and bring up the font panel, the first selection I make in the fontPanel results in deselection of the text.
Making my view controller the target for NSFontManager and implementing a stub for changeAttributes yields an object of type NSFontEffectsBox which I am unable to find any good documentation for.
Question is... what am I supposed to do with NSFontEffectsBox? If in the fontPanel I select blue text with double underline, I can see those attributes in the debugger but I'm unable to access them programatically.
Here's the relevant code:
override func viewDidLoad() {
super.viewDidLoad()
loadNoteIntoInterface()
noteBody.keyDelegate = self // noteBody is the NSTextView
noteBody.delegate = self
noteBody.usesFontPanel = true
fontManager = NSFontManager.sharedFontManager()
fontManager!.target = self
}
Code for changing the font. This works just fine.
override func changeFont(sender: AnyObject?) {
let fm = sender as! NSFontManager
if noteBody.selectedRange().length>0 {
let theFont = fm.convertFont((noteBody.textStorage?.font)!)
noteBody.textStorage?.setAttributes([NSFontAttributeName: theFont], range: noteBody.selectedRange())
}
}
Stub code for changeAttributes:
func changeAttributes(sender: AnyObject) {
print(sender)
}
So.. my goals are two:
Understand what's going on here
have any changes I make in the fontPanel be reflected in the NSTextView selected text.
Thank you.
So I did manage to find an answer of sorts. Here's how I implemented changeAttributes() in my program:
func changeAttributes(sender: AnyObject) {
var newAttributes = sender.convertAttributes([String : AnyObject]())
newAttributes["NSForegroundColorAttributeName"] = newAttributes["NSColor"]
newAttributes["NSUnderlineStyleAttributeName"] = newAttributes["NSUnderline"]
newAttributes["NSStrikethroughStyleAttributeName"] = newAttributes["NSStrikethrough"]
newAttributes["NSUnderlineColorAttributeName"] = newAttributes["NSUnderlineColor"]
newAttributes["NSStrikethroughColorAttributeName"] = newAttributes["NSStrikethroughColor"]
print(newAttributes)
if noteBody.selectedRange().length>0 {
noteBody.textStorage?.addAttributes(newAttributes, range: noteBody.selectedRange())
}
}
Calling convertAttributes() on sender returns an attribute array, but the names don't seem to be what NSAttributedString is looking for. So I just copy them from old name to new and send them on. This is a good start, but I will probably remove the old keys before adding the attributes.
The question remains, tho.. is this the right way to do things?