UIScrollView with UIView Can't Select Any Subview Items - ios

I currently have a UIViewController setup like this, and I'm unable to interact with the controls in the contentView.
I'm currently following this concept: https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithScrollViews.html
class SimpleViewController: UIViewController {
override func viewDidLoad() {
let scrollView: UIScrollView = {
let newScrollView = UIScrollView()
newScrollView.translatesAutoresizingMaskIntoConstraints = false
let safeGuide = view.safeAreaLayoutGuide
self.view.addSubview(newScrollView)
newScrollView.topAnchor.constraint(equalTo: safeGuide.topAnchor).isActive = true
newScrollView.leadingAnchor.constraint(equalTo: safeGuide.leadingAnchor).isActive = true
newScrollView.trailingAnchor.constraint(equalTo: safeGuide.trailingAnchor).isActive = true
newScrollView.bottomAnchor.constraint(equalTo: safeGuide.bottomAnchor).isActive = true
return newScrollView
}()
let contentView: UIView = {
let newView = UIView()
newView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(newView)
newView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0).isActive = true
newView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 0).isActive = true
newView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: 0).isActive = true
newView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
return newView
}()
let createBtn: UIButton = {
let btn = UIButton()
btn.setTitle("Create", for: UIControl.State.normal)
btn.backgroundColor = .orange;
btn.translatesAutoresizingMaskIntoConstraints = false;
contentView.addSubview(btn)
btn.layer.cornerRadius = 6.0;
btn.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10);
btn.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
btn.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
btn.widthAnchor.constraint(equalToConstant: 100).isActive = true
return btn
}()
let cancelBtn: UIButton = {
let btn = UIButton()
btn.setTitle("Cancel", for: UIControl.State.normal)
btn.backgroundColor = .red;
btn.translatesAutoresizingMaskIntoConstraints = false;
contentView.addSubview(btn)
btn.layer.cornerRadius = 6.0;
btn.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10);
btn.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
btn.leadingAnchor.constraint(equalTo: createBtn.trailingAnchor).isActive = true
btn.widthAnchor.constraint(equalToConstant: 100).isActive = true
btn.addTarget(self, action: #selector(self.cancelCreateOrder), for: .touchUpInside);
return btn
}()
}
}
If I were to add the buttons directly to the scrollView, then I can interact with them. However, with them in the contentView, it's not registering any clicks.

Add 2 constraints
// contentView should tell scrollView it's width ( setting the leading and trailing isn't enough what matters the width to be inside the scrollView which is still un-known even after hooking the leading & trailing )
newView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: 0).isActive = true
And
// contentView should tell scrollView it's height by top & bottom constraints hooking
btn.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true

Related

Setting UITextField heightAnchor to 0 does not work on iOS 15

I have an iOS application where i have a textfield and a button and on tap of button i have to hide the textfield.
I am setting heightAnchor to 0 on tap of button. Everything is working fine on iOS 14(14.5) but does not work(does not hide the text field) on iOS 15. Also, I have tried setting up the isHidden property on UITextField but it does not work.
Can you please help tell if something changed or i am doing something wrong. Thank you.
Code reference:
import UIKit
class ViewController: UIViewController {
private lazy var mytextFeild: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.text = "Hello world"
textField.backgroundColor = .green
return textField
}()
private lazy var testView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemPink
return view
}()
private lazy var button: UIButton = {
let view = UIButton()
view.backgroundColor = .blue
view.setTitle("hide it", for: .normal)
view.translatesAutoresizingMaskIntoConstraints = false
view.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
return view
}()
var heightConstraint: NSLayoutConstraint?
#objc func buttonTapped() {
heightConstraint?.isActive = false
heightConstraint = mytextFeild.heightAnchor.constraint(equalToConstant: 0)
heightConstraint?.isActive = true
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mytextFeild)
view.addSubview(testView)
view.addSubview(button)
mytextFeild.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32).isActive = true
mytextFeild.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32.0).isActive = true
mytextFeild.topAnchor.constraint(equalTo: view.topAnchor, constant: 64).isActive = true
heightConstraint = mytextFeild.heightAnchor.constraint(equalToConstant: 32.0)
heightConstraint?.isActive = true
button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32.0).isActive = true
button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32.0).isActive = true
button.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -64.0).isActive = true
testView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
testView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
testView.topAnchor.constraint(equalTo: mytextFeild.bottomAnchor).isActive = true
testView.bottomAnchor.constraint(equalTo: button.topAnchor).isActive = true
}
}
Add them to a stack then add stack to the viewController. at the end try to hide it easily without changing the height.
class ViewController: UIViewController {
private lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
stackView.alignment = .center
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
private lazy var myTextField: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.text = "Hello world"
textField.backgroundColor = .green
return textField
}()
private lazy var testView: UIView = {
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemPink
return view
}()
private lazy var button: UIButton = {
let view = UIButton()
view.backgroundColor = .blue
view.setTitle("hide it", for: .normal)
view.translatesAutoresizingMaskIntoConstraints = false
view.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
return view
}()
#objc func buttonTapped() {
myTextField.isHidden = !myTextField.isHidden
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
stackView.addArrangedSubview(myTextField)
stackView.addArrangedSubview(testView)
stackView.addArrangedSubview(button)
view.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -64.0).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
myTextField.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
myTextField.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -64.0).isActive = true
testView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
button.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
button.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -64.0).isActive = true
}
}

Need to add Label on UIView programatically based on some conditions using swift

I am working on a canvas view where I am trying to add image to a UIView and a text to a UIView, on satisfying some conditions.
The issue is, I am not able to add label to my UIView after satisfying the required condition. I tried this code separately without any functions and conditions and it is working fine. But if I try and run it as given below it doesn't show me the label on the UIView.
[Note- The code here is working fine for imageView.]
I tried various constraint combinations like, centerX, centerY, width, height or leading, trailing, top, bottom. But nothing is working. Please help.
My code is as below: -
class CanvasViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
createCanvasOnAspectRatio()
}
func createCanvasOnAspectRatio() {
let View1: UIView = {
let viewView = UIView()
viewView.translatesAutoresizingMaskIntoConstraints = false
viewView.contentMode = .scaleAspectFit
print(UserDefaults.standard.bool(forKey: "backgroundColourSelected"))
if UserDefaults.standard.bool(forKey: "backgroundColourSelected") {
viewView.backgroundColor = self.viewColor
}else {
viewView.backgroundColor = .white
}
viewView.clipsToBounds = true
return viewView
}()
self.canvasView.addSubview(View1)
View1.centerXAnchor.constraint(equalTo: canvasView.centerXAnchor, constant: 0).isActive = true
View1.centerYAnchor.constraint(equalTo: canvasView.centerYAnchor, constant: 0).isActive = true
View1.widthAnchor.constraint(equalTo: canvasView.widthAnchor, constant: 0).isActive = true
View1.heightAnchor.constraint(equalTo: canvasView.widthAnchor, multiplier: aspectRatio).isActive = true
if UserDefaults.standard.bool(forKey: "imageSelectedFromGallexy") == true {
for item in 0..<self.imagesForGallery.count {
let image = imagesForGallery[item]
let image_View: UIImageView = {
let imageV = UIImageView()
imageV.image = image
imageV.contentMode = .scaleAspectFill
imageV.translatesAutoresizingMaskIntoConstraints = false
imageV.clipsToBounds = true
return imageV
}()
View1.addSubview(image_View)
image_View.topAnchor.constraint(equalTo: View1.topAnchor, constant: 0).isActive = true
image_View.bottomAnchor.constraint(equalTo: View1.bottomAnchor, constant: 0).isActive = true
image_View.leadingAnchor.constraint(equalTo: View1.leadingAnchor, constant: 0).isActive = true
image_View.trailingAnchor.constraint(equalTo: View1.trailingAnchor, constant: 0).isActive = true
}
}
if UserDefaults.standard.bool(forKey: "addTextToCanvas") == true {
let labelTextView: UILabel = {
let labelView = UILabel()
//labelView.frame = CGRect(x: 0.0, y: 0.0, width: 50.0, height: 30.0)
labelView.translatesAutoresizingMaskIntoConstraints = false
labelView.textColor = .green
labelView.text = "Hello"
labelView.font = UIFont(name: "Avenir-Medium", size: 15.0)
labelView.textAlignment = .center
// labelView.clipsToBounds = true
return labelView
}()
View1.addSubview(labelTextView)
labelTextView.centerXAnchor.constraint(equalTo: View1.centerXAnchor, constant: 0).isActive = true
labelTextView.centerYAnchor.constraint(equalTo: View1.centerYAnchor, constant: 0).isActive = true
labelTextView.widthAnchor.constraint(equalTo: View1.widthAnchor, constant: 0).isActive = true
labelTextView.heightAnchor.constraint(equalTo: View1.widthAnchor, multiplier: 1.0).isActive = true
UserDefaults.standard.set(false, forKey: "addTextToCanvas")
}
}
}

Copy of UiView and all subviews..Copied UIButton cannot be pressed

Essentially I am using this code extension below to copy a view and all its subviews. The copy is successful and I am able to view the copied view. However, the button on each copied view cannot be pressed. The button can only be pressed within the first original (not copied) view.
How to get all copied buttons to be active? Is this even possible?
I have already tried .isUserInteractionEnabled on the button and its parent view.
override func viewDidLoad(){
super.viewDidLoad()
view.isUserInteractionEnabled = true
view.addSubview(containerScrollView)
containerScrollView.addSubview(contentView)
contentView.addSubview(stackMainView)
let button = UIButton(frame: CGRect(x: 270, y: 200, width: 80, height: 40))
let partLabel1 = UILabel(frame: CGRect(x:10, y: 10, width: 300, height: 50))
let partLabel2 = UILabel(frame: CGRect(x:10, y: 50, width: 300, height: 50))
partLabel1.text = "This should sit within part use :)"
partLabel1.textColor = .white
partLabel2.text = "This should also sit within part use :)"
partLabel2.textColor = .white
contentView.addSubview(button)
contentView.addSubview(partLabel1)
contentView.addSubview(partLabel2)
part.addSubview(button)
part.addSubview(partLabel1)
part.addSubview(partLabel2)
part.bringSubviewToFront(button)
part.bringSubviewToFront(partUse3Label1)
part.layer.zPosition = -1
button.setTitle("Issue", for: .normal)
button.backgroundColor = .orange
button.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
button.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
button.bottomAnchor.constraint(equalTo: part.bottomAnchor).isActive = true
button.topAnchor.constraint(equalTo: part.topAnchor).isActive = true
button.heightAnchor.constraint(equalToConstant: 40).isActive = true
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
button.isUserInteractionEnabled = true
part.bringSubviewToFront(button)
partLabel1.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
partLabel1.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
partLabel2.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
partLabel2.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
part.layoutIfNeeded()
let copiedView = self.part.copyView()
stackMainView.addArrangedSubview(part)
stackMainView.addArrangedSubview(copiedView)
containerScrollView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
containerScrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0).isActive = true
containerScrollView.trailingAnchor.constraint(equalTo:self.view.trailingAnchor, constant: 0).isActive = true
containerScrollView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
contentView.topAnchor.constraint(equalTo: self.containerScrollView.topAnchor, constant: 0).isActive = true
contentView.leadingAnchor.constraint(equalTo: self.containerScrollView.leadingAnchor, constant: 0).isActive = true
contentView.trailingAnchor.constraint(equalTo:self.containerScrollView.trailingAnchor, constant: 0).isActive = true
contentView.bottomAnchor.constraint(equalTo: self.containerScrollView.bottomAnchor, constant: 0).isActive = true
contentView.widthAnchor.constraint(equalTo:self.view.widthAnchor, constant: 0).isActive = true
stackMainView.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 50).isActive = true
stackMainView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 8).isActive = true
stackMainView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -8).isActive = true
stackMainView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -30).isActive = true
}
#objc func buttonAction(sender: UIButton!) {
print("Button tapped")
}
var containerScrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .white
scrollView.isScrollEnabled = true
return scrollView
}()
var contentView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.white
return view
}()
let stackMainView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.backgroundColor = .random()
return stackView
}()
let part: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.cornerRadius = 4
view.layer.masksToBounds = true
view.backgroundColor = .random()
return view
}()
Extension to copy the view.
extension UIView {
func copyView<T: UIView>() -> T {
return NSKeyedUnarchiver.unarchiveObject(with:
NSKeyedArchiver.archivedData(withRootObject: self)) as! T
}
}
I expect the output to print in the console "Button Tapped"
This happens only when I press the button on the non copied view.
Since this is all done in viewDidLoad, I assume that the view you want to copy is the same every time.
Your code does not work likely because NSKeyedArchiver does not archive the button's target and selector pairs.
You can create a method that gives a new UIView instead:
func createPart() -> UIView {
let part = UIView()
part.translatesAutoresizingMaskIntoConstraints = false
part.layer.cornerRadius = 4
part.layer.masksToBounds = true
part.backgroundColor = .random()
// The part below is copied from your viewDidLoad method
// Include only those lines that create the part view.
// I might have put more than you need. Check twice
let button = UIButton(frame: CGRect(x: 270, y: 200, width: 80, height: 40))
let partLabel1 = UILabel(frame: CGRect(x:10, y: 10, width: 300, height: 50))
let partLabel2 = UILabel(frame: CGRect(x:10, y: 50, width: 300, height: 50))
partLabel1.text = "This should sit within part use :)"
partLabel1.textColor = .white
partLabel2.text = "This should also sit within part use :)"
partLabel2.textColor = .white
part.addSubview(button)
part.addSubview(partLabel1)
part.addSubview(partLabel2)
part.bringSubviewToFront(button)
part.bringSubviewToFront(partUse3Label1)
part.layer.zPosition = -1
button.setTitle("Issue", for: .normal)
button.backgroundColor = .orange
button.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
button.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
button.bottomAnchor.constraint(equalTo: part.bottomAnchor).isActive = true
button.topAnchor.constraint(equalTo: part.topAnchor).isActive = true
button.heightAnchor.constraint(equalToConstant: 40).isActive = true
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
button.isUserInteractionEnabled = true
part.bringSubviewToFront(button)
partLabel1.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
partLabel1.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
partLabel2.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
partLabel2.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
part.layoutIfNeeded()
return part
}
And then in viewDidLoad, you should remove the lines of code that help create the part view, leaving only the code that creates the stack view and main content view. You should then call createPart twice, and there you have 2 copies!
let part = createPart()
let copyOfPart = createPart()

Setting up UI constraint (Image and stackView inside a UIView) programmatically in Swift

I'm trying to build a custom AD when the app opens it pop up some UIViews and image and two buttons then control it from my Firebase, for now I have problem adding the adImage and buttonsStack(contains 2 buttons) inside my backView programmatically and so far nothing works ..
I need the image takes ~ %75 of the backView up and the buttonsStack ~ %25 of the rest
here some code and I have upload it to my GitHub
import UIKit
class ViewController: UIViewController {
let backroundView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .black
view.alpha = 0.5
return view
}()
let backView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
view.layer.cornerRadius = 15
return view
}()
let adImage: UIImageView = {
var image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.contentMode = .scaleAspectFill
return image
}()
let buttonsStack: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.alignment = UIStackViewAlignment.fill
stack.axis = UILayoutConstraintAxis.vertical
stack.distribution = .equalSpacing
stack.spacing = 8
stack.backgroundColor = UIColor.red
return stack
}()
let actionButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Open", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = UIColor(red: 0, green: 0.60, blue: 1, alpha: 1)
button.layer.cornerRadius = 8
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.textAlignment = .center
return button
}()
let dismessButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Exit", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .lightGray
button.layer.cornerRadius = 8
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.textAlignment = .center
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
func setupUI(){
// backroundView
view.addSubview(backroundView)
backroundView.frame = view.frame
// backView
view.addSubview(backView)
backView.topAnchor.constraint(equalTo: view.topAnchor, constant: 80).isActive = true
backView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50).isActive = true
backView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -25).isActive = true
backView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 25).isActive = true
// adImage
backView.addSubview(adImage)
adImage.image = UIImage(named: "testImage")
adImage.topAnchor.constraint(equalTo: backView.topAnchor).isActive = true
adImage.rightAnchor.constraint(equalTo: backView.rightAnchor).isActive = true
adImage.leftAnchor.constraint(equalTo: backView.leftAnchor).isActive = true
adImage.heightAnchor.constraint(equalTo: backView.heightAnchor, multiplier: 0.50).isActive = true
// buttonsStack
buttonsStack.addArrangedSubview(actionButton)
buttonsStack.addArrangedSubview(dismessButton)
backView.addSubview(buttonsStack)
buttonsStack.topAnchor.constraint(equalTo: backView.topAnchor, constant: 15).isActive = true
buttonsStack.bottomAnchor.constraint(equalTo: backView.bottomAnchor, constant: -15).isActive = true
buttonsStack.rightAnchor.constraint(equalTo: backView.rightAnchor, constant: -15).isActive = true
buttonsStack.leftAnchor.constraint(equalTo: backView.leftAnchor, constant: 15).isActive = true
}
}
For the image to take 0.75 change this
adImage.heightAnchor.constraint(equalTo: backView.heightAnchor, multiplier: 0.50).isActive = true
to
adImage.heightAnchor.constraint(equalTo: backView.heightAnchor, multiplier: 0.75).isActive = true
//
then the buttonStack should goes under it so change this
buttonsStack.topAnchor.constraint(equalTo: backView.topAnchor, constant: 15).isActive = true
to
buttonsStack.topAnchor.constraint(equalTo: adImage.bottomAnchor, constant: 15).isActive = true

Creating an Input Accessory View programmatically

I'm trying to create a keyboard accessory view programmatically. I've set up a container view and inside that I'm trying to set up a textfield, post button, and an emoji.
Here's an example of what I'm trying to make.
Click here to view the image.
Here's the code that I am working with. I think the problem is when I'm setting the constraints.
Couple of questions running through my mind are:
Do I need to set up constraints for the container view?
How do I add appropriate constraints to the textfield?
override var inputAccessoryView: UIView? {
get {
//Set up the container
let containerView = UIView()
containerView.backgroundColor = #colorLiteral(red: 0.9784782529, green: 0.9650371671, blue: 0.9372026324, alpha: 1)
containerView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 60)
let textField = UITextField()
textField.placeholder = "Add a reframe..."
textField.isSecureTextEntry = false
textField.textAlignment = .left
textField.borderStyle = .none
textField.translatesAutoresizingMaskIntoConstraints = false
textField.translatesAutoresizingMaskIntoConstraints = false
textField.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
textField.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
textField.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
textField.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
textField.heightAnchor.constraint(equalToConstant: 50)
containerView.addSubview(textField)
return containerView
}
}
This is the error that I keep getting.
*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors and because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.'
EDIT:
View Post Controller
lazy var containerView: CommentInputAccessoryView = {
let frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 60)
let commentInputAccessoryView = CommentInputAccessoryView(frame: frame)
commentInputAccessoryView.delegate = self
return commentInputAccessoryView
}()
//Setting up the keyboard accessory view for comments.
override var inputAccessoryView: UIView? {
get {
return containerView
}
}
override var canBecomeFirstResponder: Bool {
return true
}
CommentInputAccessoryView
protocol CommentInputAccessoryViewDelegate {
func didSend(for comment: String)
}
class CommentInputAccessoryView: UIView {
var delegate: CommentInputAccessoryViewDelegate?
func clearCommentTextView() {
commentTextView.text = nil
showPlaceholderLabel()
}
let commentTextView: UITextView = {
let text = UITextView()
text.translatesAutoresizingMaskIntoConstraints = false
//text.placeholder = "Add a reframe..."
text.textAlignment = .left
text.backgroundColor = #colorLiteral(red: 0.9784782529, green: 0.9650371671, blue: 0.9372026324, alpha: 1)
text.layer.cornerRadius = 50/2
text.layer.masksToBounds = true
text.isScrollEnabled = false
text.font = UIFont.systemFont(ofSize: 16)
text.textContainerInset = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 64)
//text.borderStyle = .none
return text
}()
let placeholderLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Add a response..."
label.textColor = .black
label.font = UIFont.systemFont(ofSize: 16)
return label
}()
func showPlaceholderLabel() {
placeholderLabel.isHidden = false
}
let sendButton: UIButton = {
let send = UIButton(type: .system)
//let sendButton = UIImageView(image: #imageLiteral(resourceName: "arrowUp"))
send.translatesAutoresizingMaskIntoConstraints = false
send.setTitle("Send", for: .normal)
send.setTitleColor(#colorLiteral(red: 0.2901960784, green: 0.3725490196, blue: 0.937254902, alpha: 1), for: .normal)
send.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
send.addTarget(self, action: #selector(handlePostComment), for: .touchUpInside)
return send
}()
let hugButton: UIButton = {
let hug = UIButton()
hug.translatesAutoresizingMaskIntoConstraints = false
hug.setTitle("🤗", for: .normal)
hug.backgroundColor = #colorLiteral(red: 0.9784782529, green: 0.9650371671, blue: 0.9372026324, alpha: 1)
hug.contentEdgeInsets = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
hug.layer.cornerRadius = 25
hug.layer.masksToBounds = true
return hug
}()
override init(frame: CGRect) {
super.init(frame: frame)
autoresizingMask = .flexibleHeight
addSubview(commentTextView)
addSubview(sendButton)
addSubview(hugButton)
addSubview(placeholderLabel)
if #available(iOS 11.0, *) {
commentTextView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
hugButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
}
else {
}
commentTextView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 8).isActive = true
commentTextView.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
commentTextView.trailingAnchor.constraint(equalTo: hugButton.leadingAnchor, constant: 8)
placeholderLabel.leadingAnchor.constraint(equalTo: commentTextView.leadingAnchor, constant: 18).isActive = true
placeholderLabel.centerYAnchor.constraint(equalTo: self.commentTextView.centerYAnchor).isActive = true
sendButton.trailingAnchor.constraint(equalTo: self.commentTextView.trailingAnchor, constant: -10).isActive = true
sendButton.centerYAnchor.constraint(equalTo: self.commentTextView.bottomAnchor, constant: -22).isActive = true
hugButton.leadingAnchor.constraint(equalTo: self.commentTextView.trailingAnchor, constant: 10).isActive = true
hugButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -8).isActive = true
hugButton.widthAnchor.constraint(equalToConstant: 40)
//hugButton.centerYAnchor.constraint(equalTo: self.commentTextView.centerYAnchor).isActive = true
NotificationCenter.default.addObserver(self, selector: #selector(handleTextChanged), name: .UITextViewTextDidChange, object: nil)
}
override var intrinsicContentSize: CGSize {
return .zero
}
#objc func handleTextChanged() {
placeholderLabel.isHidden = !self.commentTextView.text.isEmpty
}
#objc func handlePostComment() {
guard let commentText = commentTextView.text else {return}
delegate?.didSend(for: commentText)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here are some photos that might help for what is happening.
InputAccessoryView working:
Click here
TextView expansion:
Click here.
Before applying constraints view should be in view hierarchy or error which you got will be raised. To get rid of error just do containerView.addSubview(textField) after let textField = UITextField().
Regarding example image you posted, initial solution could be something like this
override var inputAccessoryView: UIView? {
get {
//Set up the container
let containerView = UIView()
containerView.backgroundColor = #colorLiteral(red: 0.9784782529, green: 0.9650371671, blue: 0.9372026324, alpha: 1)
containerView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 60)
let textField = UITextField()
containerView.addSubview(textField)
textField.translatesAutoresizingMaskIntoConstraints = false
textField.placeholder = "Add a reframe..."
textField.textAlignment = .left
textField.backgroundColor = .white
textField.layer.cornerRadius = 50/2
textField.layer.masksToBounds = true
textField.borderStyle = .none
textField.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 8).isActive = true
textField.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -5).isActive = true
textField.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 10).isActive = true
textField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 15, height: textField.frame.height)) // adding left padding so it's not sticked to border
textField.leftViewMode = .always
let arrow = UIImageView(image: #imageLiteral(resourceName: "arrowUp"))
containerView.addSubview(arrow)
arrow.translatesAutoresizingMaskIntoConstraints = false
arrow.trailingAnchor.constraint(equalTo: textField.trailingAnchor, constant: -10).isActive = true
arrow.centerYAnchor.constraint(equalTo: textField.centerYAnchor).isActive = true
let button = UIButton()
containerView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("🤗", for: .normal)
button.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
button.leadingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 10).isActive = true
button.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8).isActive = true
button.centerYAnchor.constraint(equalTo: textField.centerYAnchor).isActive = true
// Negative values for constraints can be avoided if we change order of views when applying constrains
// f.e. instead of button.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8).isActive = true
// write containerView.trailingAnchor.constraint(equalTo: button.trailingAnchor, constant: 8).isActive = true
return containerView
}
}

Resources