IQKeyboardManager doesn’t work when using safeAreaLayoutGuide - ios

I've got a button whose top anchor is: backButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 2).isActive = true
And I've got a text view whose top anchor is: storyDescription.topAnchor.constraint(equalTo: numOfChapters.bottomAnchor, constant: 16).isActive = true (I've got elements between these but they don't affect this unusual behaviour).
When I tap on the textView it sometimes goes above the view, this only happens when I use view.safeAreaLayoutGuide, But when I don't use view.safeAreaLayoutGuide, it doesn't happen. And again, This only happens when I use view.safeAreaLayoutGuide and all the elements below are connected to the element that uses safeAreaLayoutGuide.

Related

Issue With Overlapping UIViews and Interactivity with UIKit/Swift

I am building an interface in Swift and UIKit targeting iOS. All of my views are programmatically constructed. I am having an issue where a UIView that overlaps another UIView (but does not completely cover it) prevents any tap events from passing through. The layout looks like this:
The navbar at the bottom is a custom view and functions fine. Tapping those buttons changes the active view behind it (the current view is the TextView with the SQL syntax highlighting). Above it is a UILabel (with the text "Connected") and a UIView (the circle with the gradient background). I will refer to this element as the "ButtonView" and the label as the "StatusView". The "StatusView" is anchored to the navbar and the enclosing view, and the "ButtonView" is anchored to the navbar and the "StatusView".
This is the relevant layout code:
view.addSubview(navBar!)
view.addSubview(status)
view.addSubview(button)
status.translatesAutoresizingMaskIntoConstraints = false
status.layer.masksToBounds = true
status.text = "Example String"
status.textColor = .white
status.layer.cornerRadius = 20.0
status.layoutMargins = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)
button.translatesAutoresizingMaskIntoConstraints = false
button.layer.masksToBounds = true
button.layer.cornerRadius = 35.0
// button.isUserInteractionEnabled = false
// button.backgroundColor = .white
NSLayoutConstraint.activate([
status.bottomAnchor.constraint(equalTo: navBar!.topAnchor, constant: -10.0),
status.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10.0),
status.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -90.0),
status.heightAnchor.constraint(equalToConstant: 50.0),
button.bottomAnchor.constraint(equalTo: navBar!.topAnchor, constant: -10.0),
button.leadingAnchor.constraint(equalTo: status.trailingAnchor, constant: 10.0),
button.widthAnchor.constraint(equalToConstant: 70.0),
button.heightAnchor.constraint(equalToConstant: 70.0)
])
The intended behavior is that even with the "ButtonView" present, the SQL view can still be tapped and edited. However, when the "ButtonView" is present, the SQL view can no longer be focused. If the "ButtonView"'s isUserInteractionEnabled property is set to false or the "ButtonView" is removed, everything behaves correctly. The "StatusView" does not seem to have any adverse effects, without having to edit any of its properties. (Note that there is no gestural behavior currently assigned to the "ButtonView", and that is not a UIButton). The debug view does not seem to show any overlapping layers:
Ideally, I would like to eventually add a gesture recognizer to this view, but wondering if that will forever disallow me from accessing the views that are further in the background? Is it possible to overlap interactive views like this, or am I somehow screwing up the responder chain?

View position glitches around in scroll view when scrolling

Whenever you scroll, the title view bugs out and rapidly switches between two positions. The label manages to stay within the constraints of the view, and it is just the view's position that bugs.
I've made a gif of the issue if it is still unclear or you need to see an example of the problem.
https://gph.is/g/EJ09l9Z
I've tried playing around with constraints to no avail. I still believe that the issue is to do with constraints, although I am not an expert by any means in Xcode or swift so I could be wildly wrong.
These are the constraints that I have been working with and around in attempts to get this working.
titleView.leftAnchor.constraint(equalTo: scrollView.contentView.leftAnchor).isActive = true
titleView.topAnchor.constraint(equalTo: scrollView.contentView.topAnchor, constant: 6).isActive = true
titleView.rightAnchor.constraint(equalTo: scrollView.contentView.rightAnchor, constant: 6).isActive = true
titleView.bottomAnchor.constraint(equalTo: timeView.topAnchor).isActive = true
titleLabel.leftAnchor.constraint(equalTo: titleView.leftAnchor, constant: 10).isActive = true
titleLabel.topAnchor.constraint(equalTo: titleView.topAnchor).isActive = true
titleLabel.rightAnchor.constraint(equalTo: titleView.rightAnchor).isActive = true
titleLabel.bottomAnchor.constraint(equalTo: timeView.topAnchor).isActive = true
titleLabel.layer.masksToBounds = true
The goal is for the title to stay in the one position as the view is scrolled, like the labels and views beneath it. Feel free to suggest any other fixes or ask for more code, I just don't know what the problem would be if not constraints.
Also for clarification, I am not using a UIScrollView, but rather an OLEContainerScrollView.
https://github.com/ole/OLEContainerScrollView
https://oleb.net/blog/2014/05/scrollviews-inside-scrollviews/

How to make a UITextView fill up the remaining space

I have a UIButton and UITextField side by side as follows.
I want the Add button to only have a width based on the content. And the text view shall take up the rest of the space. I have the following auto layout constraints.
private func setupLayout() {
newDeviceIdTextField.topAnchor.constraint(equalTo: deviceIdLabel.bottomAnchor, constant: 12).isActive = true
newDeviceIdTextField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 12).isActive = true
newDeviceIdButton.topAnchor.constraint(equalTo: deviceIdLabel.bottomAnchor, constant: 12).isActive = true
newDeviceIdButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -12).isActive = true
newDeviceIdButton.leadingAnchor.constraint(equalTo: newDeviceIdTextField.trailingAnchor, constant: 0).isActive = true
}
In this article there is a section which I believe exactly solves my problem. But I just don't understand how.
What am I missing here?
A button automatically sizes its width to fit its content, and both a text field and a button have an automatic height. So what you want to do is trivial:
Pin the left of the text field where you want it.
Pin the right of the button where you want it.
Pin the tops of both where you want them.
Pin the right of the text field to the left of the button and set that constant to a small number such as 8.
You can do this without writing code, from storyboard. What i do for this kind of UI is,
Pin the left of the text field where you want it.
Pin the right of the button where you want it.
Pin the tops of both where you want them.
Pin the right of the text field to the left of the button
It's look like in image below
Now Select button > Size Inspector > In Content hugging Priority
Change Horizontal to 750(high) and you done :)
Result look like in image below

setting textfield constraints to hold effective in screen sizes 4.5 in to 6.5 in

if I set fixed width it either appears too large for small screen (4.5 in) or too small for large screen (6.5 in)
and
is there any special way to ensure the constraints hold good in all
constraints
Like Jatin mentioned in the comments, you can use leading and trailing anchors relative to the view like this,
textField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
textField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
Or, you could set the width as a multiplier to the width of the view.
textField.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.80).isActive = true
Note: Change the constant and multiplier values to suit your needs.

NSLayoutConstraint rules

I can't see the menu bar (The blue view) when i write this code, but when i change the parameter from 50 to 100 its shows. It seems like it lies behind the status field. I want the constraints to relate to the status bar not the screens top. Someone who knows why?
func setupMenuBar(){
view.addSubview(menuBar)
view.addConstriantswithFormat(format: "H:|[v0]|", views:menuBar)
view.addConstriantswithFormat(format: "V:|[v0(50)]", views:menuBar)
}
You need to constrain your menuBar view to the view's safe area to get it to align with the bottom of the navigation bar.
Tough to do with Visual Format Language though. This alternative method should be easy to understand:
view.addSubview(menuBar)
let guide = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
menuBar.topAnchor.constraint(equalTo: guide.topAnchor, constant: 0.0),
menuBar.leadingAnchor.constraint(equalTo: guide.leadingAnchor, constant: 0.0),
menuBar.trailingAnchor.constraint(equalTo: guide.trailingAnchor, constant: 0.0),
menuBar.heightAnchor.constraint(equalToConstant: 50.0),
])
If the red view is a navigation bar, you should probably look at not extending edges under top bar; otherwise, you need to constrain the blue view in accordance to the red view.
I prefer using the anchors. It is much easier to read.
For example (Swift):
blueView.topAnchor.constraint(equalTo: redView.bottomAnchor).isActive = true
blueView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
blueView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
blueView.heightAnchor.constraint(equalToConstant: 50).isActive = true

Resources