Swift - addTarget to UIButton inside a Subview - ios

I have a problem with my UIButton. Just for basic understanding:
User taps on a button -> popUpView appears (UIView)
User taps on UIButton which is a SubView of popUpView -> popUpView dismisses
That is my code for that:
#objc func addWishButtonTapped(notification : Notification){
view.addSubview(popUpView)
popUpView.addSubview(popUpTextField)
popUpView.addSubview(wishButton)
// constrain popUpView
popUpView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
popUpView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -100).isActive = true
popUpView.heightAnchor.constraint(equalToConstant: 200).isActive = true
popUpView.widthAnchor.constraint(equalToConstant: view.frame.width - 85).isActive = true
// constrain wishButton
wishButton.centerXAnchor.constraint(equalTo: popUpView.centerXAnchor).isActive = true
wishButton.centerYAnchor.constraint(equalTo: popUpView.centerYAnchor, constant: 130).isActive = true
wishButton.heightAnchor.constraint(equalToConstant: 100).isActive = true
wishButton.widthAnchor.constraint(equalToConstant: 100).isActive = true
// constrain textField
popUpTextField.centerXAnchor.constraint(equalTo: popUpView.centerXAnchor).isActive = true
popUpTextField.centerYAnchor.constraint(equalTo: popUpView.centerYAnchor, constant: -50).isActive = true
popUpTextField.heightAnchor.constraint(equalToConstant: 40).isActive = true
popUpTextField.widthAnchor.constraint(equalToConstant: view.frame.width - 170).isActive = true
popUpView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
popUpView.alpha = 0
UIView.animate(withDuration: 0.3) {
self.visualEffectView.alpha = 1
self.popUpView.alpha = 1
self.popUpView.transform = CGAffineTransform.identity
}
// make whishButton clickable
self.wishButton.addTarget(self, action: #selector(wishButtonTapped), for: .touchUpInside)
}
#objc func wishButtonTapped(){
print("test")
insertWhish()
dismissPopUpView()
}
Problem:
whishButton is not clickable and I have no idea why.. I'm stuck on this for a while now so I am grateful for any help, thanks :)
UPDATE
This is my View Hierarchy:
Really weird, because the selected UIImage should actually be the UIButton which is in my case behind it (100x100 square). I definitely declared my whishButton as an actual UIButton()

first, try whit this line
self.popUpView.bringSubviewToFront(whishButton)
and if its necessary try this line
self.popUpView.sendSubview(yourImageBackGround)

Try to move buttons in storyboard

SOLVED
I solved my problem.. not in a very smooth way but it's working now. I just added the button as another subview of my normal UIView instead of adding it within my popUpView.

I guess, you don't create any instantiate of your root class globally.
class ViewController: UIViewController {
var viewController: MyViewController
override func viewDidLoad() {
super.viewDidLoad()
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
myViewController =storyBoard.instantiateViewController(withIdentifier:"MyViewController") as! MyViewController
self.present(myViewController, animated:true, completion:nil)
myViewController.addWishButtonTapped(nil)
}
}
class MyViewController: UIViewController {
let popUpView = UIView()
let popUpTextField = UITextField()
let wishButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
}
#objc func addWishButtonTapped(notification : Notification?){
view.addSubview(popUpView)
popUpView.addSubview(popUpTextField)
popUpView.addSubview(wishButton)
// constrain popUpView
popUpView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
popUpView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -100).isActive = true
popUpView.heightAnchor.constraint(equalToConstant: 200).isActive = true
popUpView.widthAnchor.constraint(equalToConstant: view.frame.width - 85).isActive = true
// constrain wishButton
wishButton.centerXAnchor.constraint(equalTo: popUpView.centerXAnchor).isActive = true
wishButton.centerYAnchor.constraint(equalTo: popUpView.centerYAnchor, constant: 130).isActive = true
wishButton.heightAnchor.constraint(equalToConstant: 100).isActive = true
wishButton.widthAnchor.constraint(equalToConstant: 100).isActive = true
// constrain textField
popUpTextField.centerXAnchor.constraint(equalTo: popUpView.centerXAnchor).isActive = true
popUpTextField.centerYAnchor.constraint(equalTo: popUpView.centerYAnchor, constant: -50).isActive = true
popUpTextField.heightAnchor.constraint(equalToConstant: 40).isActive = true
popUpTextField.widthAnchor.constraint(equalToConstant: view.frame.width - 170).isActive = true
popUpView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
popUpView.alpha = 0
UIView.animate(withDuration: 0.3) {
self.visualEffectView.alpha = 1
self.popUpView.alpha = 1
self.popUpView.transform = CGAffineTransform.identity
}
// make whishButton clickable
self.wishButton.addTarget(self, action: #selector(wishButtonTapped), for: .touchUpInside)
}
#objc func wishButtonTapped(){
print("test")
insertWhish()
dismissPopUpView()
}
}

Related

Updating layout anchors not working as expected

I have created a view with height 100 using NSLayout anchors. When I'm trying to update that on button click, it's not working.
I have tried below code, but it's not working.
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
let viewAnimate = UIView()
var isHidden = false
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(viewAnimate)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
viewAnimate.translatesAutoresizingMaskIntoConstraints = false
viewAnimate.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 8).isActive = true
viewAnimate.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -8).isActive = true
viewAnimate.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100).isActive = true
viewAnimate.heightAnchor.constraint(equalToConstant: 100).isActive = true
viewAnimate.backgroundColor = UIColor.red
}
#IBAction func show() {
if !isHidden {
viewAnimate.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 200).isActive = true
button.setTitle("Show", for: .normal)
} else {
button.setTitle("Hide", for: .normal)
viewAnimate.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100).isActive = true
}
UIView.animate(withDuration: 1) {
self.viewAnimate.layoutIfNeeded()
}
isHidden = !isHidden
}
}
View should change the height based on height constraint
Your current code creates conflicts as every line like viewAnimate.topAnchor.constraint(equalTo: adds a new constraint , create a var
var topCon:NSLayoutConstraint!
topCon = viewAnimate.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100)
topCon.isActive = true
#IBAction func show() {
if !isHidden {
topCon.constant = 200
button.setTitle("Show", for: .normal)
} else {
button.setTitle("Hide", for: .normal)
topCon.constant = 100
}
UIView.animate(withDuration: 1) {
self.view.layoutIfNeeded()
}
isHidden = !isHidden
}

How do I collapse UIViews programatically?

I'm working on my first IOS app and have run into an issue. I have a quite elaborate programmatic autoloyout UI that responds to user interaction. When a keyboard is shown certain Views must be collapsed, others moved and others spawned into existence based on a few conditions.
Now in it's default state no autolayout errors occur. But once things start moving it all comes apart. A few of the issues have to do with images retaining their height, while their view's heigconstriant is set to 0. Now I do have .scaleToFill enabled.
I have looked into stackViews however since most of my Views are of a different size with different nested UI elements stackviews do now appear to solve my issues. But I would certainly like some input on that.
Now my questions is: How do I collapse UIView and UIImageviews dynamically and programatically?
Now I don't mind typing out a lot of constraints manually, as long as it works.
Here are the constraints of the Views in question(there are more)
func setUpLayout() {
// SuggestionCloud
suggestionCloud.setConstraints(
topAnchor: textView.bottomAnchor, topConstant: 0,
bottomAnchor: bottomMenu.topAnchor, bottomConstant: 0,
trailingAnchor: view.trailingAnchor, trailingConstant: -10,
leadingAnchor: view.leadingAnchor, leadingConstant: 10)
print("Suggestion View frame :\(suggestionCloud.frame)")
//WEIGHT_IMAGE_VIEW
weigtImageView.topAnchor.constraint(equalTo: view.topAnchor, constant: 30).isActive = true
weigtImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
weigtImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
weigtImageView.heightAnchor.constraint(equalToConstant: 150).isActive = true
weigtImageView.addSubview(weightLabel);
print("Weight Image View \(weigtImageView.frame)")
//WEIGHT_LABEL
weightLabel.trailingAnchor.constraint(equalTo: weigtImageView.trailingAnchor, constant: -30).isActive = true;
weightLabel.leadingAnchor.constraint(equalTo: weigtImageView.leadingAnchor, constant: 25).isActive = true;
weightLabel.heightAnchor.constraint(equalTo: weigtImageView.heightAnchor, multiplier: 1).isActive = true;
//TEXT_VIEW
textView.topAnchor.constraint(equalTo: weigtImageView.bottomAnchor).isActive = true;
textView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
textView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true;
textView.heightAnchor.constraint(equalToConstant: 100).isActive = true;
textView.addSubview(nameTextField)
textView.addSubview(tagTextField)
textView.addSubview(setButtonView)
//TAG_CONTROLLER
tagController.heightAnchor.constraint(equalToConstant: 110).isActive = true;
tagController.topAnchor.constraint(equalTo: self.weigtImageView.bottomAnchor).isActive = true;
tagController.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant : 10).isActive = true
tagController.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -10).isActive = true
//SET_BUTTON_VIEW
setButtonView.topAnchor.constraint(equalTo: textView.topAnchor).isActive = true;
setButtonView.bottomAnchor.constraint(equalTo: textView.bottomAnchor).isActive = true;
setButtonView.trailingAnchor.constraint(equalTo: textView.trailingAnchor).isActive = true;
setButtonView.widthAnchor.constraint(equalToConstant: 110).isActive = true;
//NAME_TEXT_FIELD
nameTextField.trailingAnchor.constraint(equalTo: setButtonView.leadingAnchor, constant: -5).isActive = true
nameTextField.leadingAnchor.constraint(equalTo: textView.leadingAnchor, constant: 10).isActive = true
nameTextField.topAnchor.constraint(equalTo: textView.topAnchor, constant: 13).isActive = true
nameTextField.heightAnchor.constraint(equalToConstant: 31).isActive = true
nameTextField.layer.cornerRadius = 8
nameTextField.backgroundColor = .white;
//TAG_TEXT_FIELD
tagTextField.trailingAnchor.constraint(equalTo: setButtonView.leadingAnchor, constant: -5).isActive = true
tagTextField.leadingAnchor.constraint(equalTo: textView.leadingAnchor, constant: 10).isActive = true
tagTextField.bottomAnchor.constraint(equalTo: textView.bottomAnchor, constant: -13).isActive = true
tagTextField.heightAnchor.constraint(equalToConstant: 31).isActive = true
tagTextField.layer.cornerRadius = 8
tagTextField.backgroundColor = .white
here's the viewcontrollers setup:
class UIScaleControllerVew: UIViewController, UITextFieldDelegate, SuggenstionCloudDelegate {
let weigtImageView : UIImageView = {
var imageView = UIImageView(image: UIImage(named: "scaleVisorShadow"));
imageView.contentMode = .scaleToFill
imageView.translatesAutoresizingMaskIntoConstraints = false;
return imageView
}()
let weightLabel : UILabel = {
let label = UILabel()
label.text = "135 gr"
label.font = UIFont(name: "Avenir-Light", size: 50.0)
label.textAlignment = .right
label.translatesAutoresizingMaskIntoConstraints = false
return label
}();
let textView : UIView = {
var view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false;
return view;
}();
let setButtonView : UIImageView = {
var imageView = UIImageView(image: UIImage(named: "setButton"))
imageView.translatesAutoresizingMaskIntoConstraints = false;
return imageView;
}();
let nameTextField : UITextField = {
var textField = UITextField();
textField.tag = 2;
textField.translatesAutoresizingMaskIntoConstraints = false;
textField.addTarget(self, action: #selector(nameFieldEditingChanged(_:)), for: UIControl.Event.editingChanged)
return textField;
}();
let tagTextField : UITextField = {
var textField = UITextField();
textField.tag = 1;
textField.translatesAutoresizingMaskIntoConstraints = false;
textField.addTarget(self, action: #selector(textFieldEditingChanged(_:)), for: UIControl.Event.editingChanged)
return textField;
}();
let bottomMenu : UIView = {
var view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false;
return view;
}();
let saveButton : UIButton = {
let button = UIButton()
button.setImage(UIImage(named: "save"), for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false;
return button
}();
let microPhoneButton : UIButton = {
let button = UIButton()
button.setImage(UIImage(named: "microPhone"), for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false;
return button;
}();
let suggestionCloud : SuggenstionCloud = {
let cloud = SuggenstionCloud(image: UIImage(named: "suggestionCloud.png"))
cloud.translatesAutoresizingMaskIntoConstraints = false;
return cloud;
}();
let tagController : TagController = {
let tagController = TagController()
tagController.translatesAutoresizingMaskIntoConstraints = false
return tagController;
}()
let scaleModel = ScaleModel.init()
override func viewDidLoad() {
super.viewDidLoad()
print("UIScaleController_DidLoad")
tagTextField.delegate = self
nameTextField.delegate = self;
suggestionCloud.delegate = self;
view.backgroundColor = UIColor(hexString: "8ED7F5")
view.addSubview(weigtImageView)
view.addSubview(textView)
view.addSubview(bottomMenu);
view.addSubview(suggestionCloud)
view.addSubview(tagController)
tagController.isHidden = true;
setUpLayout()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShowNotification(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHideNotification(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
var didSetUpSuggestionCloud = false
var didSetUpTagController = false
override func viewDidLayoutSubviews() {
guard !self.didSetUpTagController else {
return
}
guard !self.didSetUpSuggestionCloud else {
return
}
self.didSetUpSuggestionCloud = true
self.didSetUpTagController = true
};
and here's the problematic code:
#objc func keyboardWillShowNotification(notification: Notification ) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
// collapse and hide bottom view
bottomMenu.contentMode = .scaleToFill;
bottomMenu.heightAnchor.constraint(equalToConstant: 0).isActive = true;
bottomMenu.isHidden = true
// collapse and hide top view
weigtImageView.contentMode = .scaleToFill;
weigtImageView.heightAnchor.constraint(equalToConstant: 0).isActive = true;
weigtImageView.isHidden = true;
// spawn my tag view
tagController.topAnchor.constraint(equalTo: self.textView.bottomAnchor).isActive = true;
tagController.bottomAnchor.constraint(equalTo: suggestionCloud.topAnchor).isActive = true
tagController.isHidden = false;
// set textviews new constraints
textView.bottomAnchor.constraint(equalTo: tagController.topAnchor).isActive = true;
// set middleView's new constraints
suggestionCloud.topAnchor.constraint(equalTo: tagController.bottomAnchor).isActive = true;
suggestionCloud.bottomAnchor.constraint(equalTo: bottomMenu.topAnchor, constant: -keyboardSize.height).isActive = true
self.view.layoutIfNeeded()
}
}
Now there are so many unexpected things happening that i'm positive that my approach to this is just wrong conceptually.
Please let me know where I need to look for a solution.
Here are e few pictures of what is happening so far:
So when the keyboard is up:
The weightView is collapsed: suggestioncloud and text are moved up.
If a tag is added a new view called tagController needs to be places between the texView and the suggesitonCloud. Lastyl the keybaord needs to be collapsed again.
Ill add some sscreenshots
One thing that you could look at is duplicate constraints.
Every time you call weigtImageView.heightAnchor.constraint(equalToConstant: 0).isActive = true you are creating a new constraint. This does not automatically replace any previous height constraints that are active.
To replace a constraint, you need to keep a reference to it, deactivate it, and then activate a new one (and optionally assign it the the variable you use to keep a reference).
Stack views
Stack views could be helpful in your situation, because they automatically collapse views that have isHidden set to true. I think that as long as the direct subviews of the StackView have an intrinsic content size (e.g. correct internal constraints), they should be placed correctly by the StackView.
If you do not have a strong reference to your views to be released then it is enough to execute this:
if view2Breleased.superview != nil {
view2Breleased.removeFromSuperview()
}
The view will then disappear and be released from memory.
If you don't know what a strong reference is then for the time being, just try the code I wrote. The views will disappear anyway.
(Strong reference means you have assigned the view to a variable which survives the execution of the code view2Breleased.removeFromSuperview() and the exit from the function call where the code view2Breleased.removeFromSuperview() is.)
You can change the constant of the heightAnchor constraint like so:
import Foundation
import UIKit
class TestController : UIViewController {
var myViewHeightConstraint : NSLayoutConstraint!
let myView : UIControl = {
let newView = UIControl()
newView.translatesAutoresizingMaskIntoConstraints = false
newView.backgroundColor = .red
return newView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
self.myView.addTarget(self, action: #selector(viewClicked), for: .touchUpInside)
self.myViewHeightConstraint = myView.heightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.heightAnchor)
setup()
}
func setup(){
view.addSubview(myView)
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
myView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
self.myViewHeightConstraint.isActive = true
}
#objc func viewClicked() {
self.myViewHeightConstraint.constant = -self.myView.frame.size.height
}
}
In my example, I set the constant to minus the height of the view's frame height, which effectively collapses it.

Mask UIView with another UIView

Yes this question has been asked before, the solutions did not work or had different applications.
It is the most basic setup. I have two rectangular UIViews, red and blue.
I would like the blue square to cut into the red square, so the red square looks like an "L"
import Foundation
import UIKit
class TestController: UIViewController {
override func viewDidLoad() {
view.backgroundColor = .gray
view.addSubview(viewA)
view.addSubview(maskView)
viewA.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
viewA.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
viewA.widthAnchor.constraint(equalToConstant: 100).isActive = true
viewA.heightAnchor.constraint(equalToConstant: 100).isActive = true
viewA.translatesAutoresizingMaskIntoConstraints = false
maskView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 50).isActive = true
maskView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -50).isActive = true
maskView.widthAnchor.constraint(equalToConstant: 100).isActive = true
maskView.heightAnchor.constraint(equalToConstant: 100).isActive = true
maskView.translatesAutoresizingMaskIntoConstraints = false
// Things which don't work
//viewA.mask = maskView // both views disappear
//viewA.layer.mask = maskView.layer // both views disappear
//viewA.layer.addSublayer(maskView.layer) // hides mask view
}
var viewA: UIView = {
let view = UIView()
view.backgroundColor = .red
view.layer.masksToBounds = true
return view
}()
var maskView: UIView = {
let view = UIView()
view.backgroundColor = .blue
return view
}()
}
This is the result I am expecting: (done in Photoshop)
As there is no magic way to mask the way in iOS, I present here a simple way to achieve this.
Don't forget to pan the clear area, If leaving the red square, it will become a blue square.
It's not hard to modify the subclass of UIViews for your own purpose, especially views.
import UIKit
class TestController: UIViewController {
override func viewDidLoad() {
view.backgroundColor = .gray
view.addSubview(viewA)
view.addSubview(maskView)
maskView.maskedView = viewA
viewA.activeMask = maskView
viewA.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
viewA.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
viewA.widthAnchor.constraint(equalToConstant: 100).isActive = true
viewA.heightAnchor.constraint(equalToConstant: 100).isActive = true
viewA.translatesAutoresizingMaskIntoConstraints = false
maskView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 50).isActive = true
maskView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -50).isActive = true
maskView.widthAnchor.constraint(equalToConstant: 100).isActive = true
maskView.heightAnchor.constraint(equalToConstant: 100).isActive = true
maskView.translatesAutoresizingMaskIntoConstraints = false
}
var viewA: MyUIView = {
let view = MyUIView()
view.backgroundColor = .clear
view.layer.masksToBounds = true
return view
}()
var maskView: ActiveMaskView = {
let view = ActiveMaskView()
view.backgroundColor = .clear
return view
}()
}
class ActiveMaskView: UIView{
override func didMoveToSuperview() {
super.didMoveToSuperview()
let panGesture = UIPanGestureRecognizer.init(target: self, action: #selector(moveAround(_:)))
self.addGestureRecognizer(panGesture)
}
weak var maskedView : UIView?
private var frameOrigin : CGPoint = CGPoint.zero
#objc func moveAround(_ panGesture: UIPanGestureRecognizer){
guard let superview = superview else {return}
switch panGesture.state {
case .began:
frameOrigin = frame.origin
self.backgroundColor = UIColor.blue
case .changed:
let translation = panGesture.translation(in: superview)
frame = CGRect.init(origin: CGPoint.init(x: frameOrigin.x + translation.x, y: frameOrigin.y + translation.y), size: frame.size)
maskedView?.setNeedsDisplay()
break
case .ended:
self.backgroundColor =
frame.intersects(maskedView!.frame) ?
UIColor.clear : UIColor.blue
maskedView?.setNeedsDisplay()
case .cancelled:
frame = CGRect.init(origin: frameOrigin , size: frame.size)
self.backgroundColor =
frame.intersects(maskedView!.frame) ?
UIColor.clear : UIColor.blue
maskedView?.setNeedsDisplay()
default:
break;
}
}
}
class MyUIView: UIView{
weak var activeMask: ActiveMaskView?
override func draw(_ rect: CGRect) {
super.draw(rect)
let ctx = UIGraphicsGetCurrentContext()
ctx?.setFillColor(UIColor.red.cgColor)
ctx?.fill(self.layer.bounds)
ctx?.setBlendMode(.sourceOut)
guard let activeMask = activeMask , let superview = superview else {
return
}
let sc = frame.intersection(activeMask.frame)
let interSection = superview.convert(sc, to: self)
ctx?.fill(interSection )
}
}

How can I show a hidden UIButton?

Views were placed using code.
If I press deleteButton (button), I want the hidden button(imgButton) to appear.
However, imgView's width is not refresh.
MainViewController.swift
import UIKit
class MainViewController: UIViewController {
var trashIsSelected: Bool!
let imgButton: UIButton = {
let imgView = UIButton()
imgView.setImage(UIImage(named: "schedule_delete_icon"), for: UIControlState.normal)
// imgView.imageView?.image = UIImage(named: "schedule_delete_icon")
imgView.translatesAutoresizingMaskIntoConstraints = false
return imgView
}()
let deleteButton: UIButton = {
let imgBtn = UIButton()
imgBtn.setImage(UIImage(named: "icon_delete"), for: UIControlState.normal)
// imgBtn.imageView?.image = UIImage(named: "icon_delete")
imgBtn.translatesAutoresizingMaskIntoConstraints = false
return imgBtn
}()
let label: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "하ㅣㅇ하이히아히아하하하하ㅏㅎ하ㅏㅎ하ㅏ하하하하ㅏㅏ하하하하하ㅏ"
return label
}()
var imgViewWidth: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
setupLayout()
}
func setupLayout(){
let testView = UIScrollView()
self.view.addSubview(testView)
testView.backgroundColor = .lightGray
testView.translatesAutoresizingMaskIntoConstraints = false
testView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
testView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
testView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
testView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.3).isActive = true
testView.addSubview(imgButton)
testView.addSubview(label)
imgButton.leadingAnchor.constraint(equalTo: testView.leadingAnchor, constant: 10).isActive = true
imgButton.centerYAnchor.constraint(equalTo: testView.centerYAnchor).isActive = true
imgButton.heightAnchor.constraint(equalTo: testView.heightAnchor, multiplier: 0.2).isActive = true
imgButton.widthAnchor.constraint(equalTo: imgButton.heightAnchor, multiplier: 0).isActive = true
imgButton.isHidden = true
trashIsSelected = false
label.centerYAnchor.constraint(equalTo: testView.centerYAnchor).isActive = true
label.leadingAnchor.constraint(equalTo: imgButton.trailingAnchor, constant: 10).isActive = true
label.heightAnchor.constraint(equalTo: testView.heightAnchor, multiplier: 0.2).isActive = true
label.widthAnchor.constraint(equalTo: testView.widthAnchor, multiplier: 0.5).isActive = true
self.view.addSubview(deleteButton)
deleteButton.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
deleteButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
deleteButton.addTarget(self, action: #selector(self.addBtnAction(_:)), for: UIControlEvents.touchUpInside)
}
#objc func addBtnAction(_ sender: UIButton){
print("hi")
if trashIsSelected == false{
trashIsSelected = true
imgButton.isHidden = false
imgButton.widthAnchor.constraint(equalToConstant: 50).isActive = true
imgButton.updateConstraints()
}else{
trashIsSelected = false
imgButton.widthAnchor.constraint(equalToConstant: 0).isActive = true
imgButton.isHidden = true
imgButton.updateConstraints()
}
}
}
This is error message:
2018-05-23 14:00:47.697959+0900 Test[67488:4887863] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"",
""
)
Will attempt to recover by breaking constraint
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in may also be helpful.
You are adding new constraints to the button that conflict with the old ones (how can a button be 0 points wide, and simultaneously 50 points wide?). To make it work, you need to turn off the old constraint before you activate a new one. I recommend creating a property that would keep the current imgButton constraint at all times, and then when you want to change it, just use that (either turn it off and create a new one, or just set a constant, which in your case is better and easier):
import UIKit
class MainViewController: UIViewController {
// property referencing current imgButton width constraint
fileprivate var imgButtonWidthConstraint: NSLayoutConstraint!
var trashIsSelected: Bool!
let imgButton: UIButton = {
let imgView = UIButton()
imgView.setImage(UIImage(named: "schedule_delete_icon"), for: UIControlState.normal)
// imgView.imageView?.image = UIImage(named: "schedule_delete_icon")
imgView.translatesAutoresizingMaskIntoConstraints = false
return imgView
}()
let deleteButton: UIButton = {
let imgBtn = UIButton()
imgBtn.setImage(UIImage(named: "icon_delete"), for: UIControlState.normal)
// imgBtn.imageView?.image = UIImage(named: "icon_delete")
imgBtn.translatesAutoresizingMaskIntoConstraints = false
return imgBtn
}()
let label: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "하ㅣㅇ하이히아히아하하하하ㅏㅎ하ㅏㅎ하ㅏ하하하하ㅏㅏ하하하하하ㅏ"
return label
}()
var imgViewWidth: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
setupLayout()
}
func setupLayout(){
let testView = UIScrollView()
self.view.addSubview(testView)
testView.backgroundColor = .lightGray
testView.translatesAutoresizingMaskIntoConstraints = false
testView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
testView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
testView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
testView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.3).isActive = true
testView.addSubview(imgButton)
testView.addSubview(label)
imgButton.leadingAnchor.constraint(equalTo: testView.leadingAnchor, constant: 10).isActive = true
imgButton.centerYAnchor.constraint(equalTo: testView.centerYAnchor).isActive = true
imgButton.heightAnchor.constraint(equalTo: testView.heightAnchor, multiplier: 0.2).isActive = true
// keep the reference to constraint that defines width
// (we will use the constraint setting the width to constant, since then you can
// simply switch the constant between 0 and 50):
imgButtonWidthConstraint = imgButton.widthAnchor.constraint(equalToConstant: 0)
imgButtonWidthConstraint.isActive = true
imgButton.isHidden = true
trashIsSelected = false
label.centerYAnchor.constraint(equalTo: testView.centerYAnchor).isActive = true
label.leadingAnchor.constraint(equalTo: imgButton.trailingAnchor, constant: 10).isActive = true
label.heightAnchor.constraint(equalTo: testView.heightAnchor, multiplier: 0.2).isActive = true
label.widthAnchor.constraint(equalTo: testView.widthAnchor, multiplier: 0.5).isActive = true
self.view.addSubview(deleteButton)
deleteButton.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
deleteButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
deleteButton.addTarget(self, action: #selector(self.addBtnAction(_:)), for: UIControlEvents.touchUpInside)
}
#objc func addBtnAction(_ sender: UIButton){
print("hi")
if trashIsSelected == false{
trashIsSelected = true
imgButton.isHidden = false
// just change the constant to what you want
imgButtonWidthConstraint.constant = 50
imgButton.updateConstraints()
} else {
trashIsSelected = false
imgButtonWidthConstraint.constant = 0
imgButton.isHidden = true
imgButton.updateConstraints()
}
}
}
EDIT:
Just for completeness of the answer, if you were for some reason using constraints where changing a constant would not be enough, you would have to activate and deactivate the constraints. E.g., if you were using multiplier to determine imgButton's width, you would have to use this approach (multiplier is an immutable property of the NSLayoutConstraint). So therefore creating a constraint:
// simply switch the constant between 0 and 50):
imgButtonWidthConstraint = imgButton.widthAnchor.constraint(equalTo: someOtherView.widthAnchor, multiplier: 0)
imgButtonWidthConstraint.isActive = true
And then in addBtnAction you would have to do to this:
#objc func addBtnAction(_ sender: UIButton){
print("hi")
if trashIsSelected == false{
trashIsSelected = true
imgButton.isHidden = false
// first deactivate current constraint
imgButtonWidthConstraint.isActive = false
// then create a new one and store it to imgButtonWidthConstraint property (the old one is deactivated, so you don't need a reference to it anymore)
imgButtonWidthConstraint = imgButton.widthAnchor.constraint(equalTo: someOtherView.widthAnchor, multiplier: 0.75)
// and activate the new one
imgButtonWidthConstraint.isActive = true
imgButton.updateConstraints()
} else {
trashIsSelected = false
// same process again
imgButtonWidthConstraint.isActive = false
imgButtonWidthConstraint = imgButton.widthAnchor.constraint(equalTo: someOtherView.widthAnchor, multiplier: 0)
imgButtonWidthConstraint.isActive = true
imgButton.isHidden = true
imgButton.updateConstraints()
}
}

safeAreaLayoutGuide to my inputAccesoryView

So with the new iPhone X, some things in my app are in the wrong position. In the bottom of my app, i have an accesoryView, which is basically an UIView with a textfield and other elements. I saw something about safeAreaLayoutGuide in the new iPhone X, but i do not now how to implement in the accessoryView. So i'm trying to find a code to implement it in my app, so the safeArea does not bother me anymore.
This is the code for the inputAccesoryView
lazy var inputContainerView: UIView = {
let containerView = UIView()
containerView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 50)
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.backgroundColor = UIColor.white
containerView.addSubview(self.inputTextField)
containerView.addSubview(self.swiche)
containerView.addSubview(self.separatorLineView)
containerView.addSubview(self.uploadImageView)
//x,y,w,h
self.inputTextField.leftAnchor.constraint(equalTo: self.swiche.rightAnchor, constant: 4).isActive = true
self.inputTextField.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
self.inputTextField.rightAnchor.constraint(equalTo: self.uploadImageView.leftAnchor, constant: 4).isActive = true
self.inputTextField.heightAnchor.constraint(equalTo: containerView.heightAnchor).isActive = true
self.inputTextField.backgroundColor = UIColor.clear
//x,y,w,h
self.swiche.leftAnchor.constraint(equalTo: containerView.leftAnchor, constant: 4).isActive = true
self.swiche.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
self.swiche.heightAnchor.constraint(equalToConstant: 30).isActive = true
self.swiche.widthAnchor.constraint(equalToConstant: 55).isActive = true
//x,y,w,h
self.uploadImageView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
self.uploadImageView.rightAnchor.constraint(equalTo: containerView.rightAnchor).isActive = true
self.uploadImageView.widthAnchor.constraint(equalToConstant: 47).isActive = true
self.uploadImageView.heightAnchor.constraint(equalToConstant: 47).isActive = true
//x,y,w,h
self.separatorLineView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true
self.separatorLineView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
self.separatorLineView.widthAnchor.constraint(equalTo: containerView.widthAnchor).isActive = true
self.separatorLineView.heightAnchor.constraint(equalToConstant: 1).isActive = true
return containerView
}()
//MARK: AccesoryView
override var inputAccessoryView: UIView? {
get {
return inputContainerView
}
}
Thanks for the help!!!
Just what I thought, all you need to do is accessing safeAreaLayoutGuide class before pointing out the constraint. In your case, you need to change constraints like these:
self.inputTextField.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
Into constraint like these:
self.inputTextField.centerYAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.centerYAnchor).isActive = true
Let me know how it goes.
ok, paste this code before lazy var
override func didMoveToWindow() {
super.didMoveToWindow()
if #available(iOS 11.0, *) {
if let window = self.window {
self.bottomAnchor.constraintLessThanOrEqualToSystemSpacingBelow(window.safeAreaLayoutGuide.bottomAnchor, multiplier: 1.0).isActive = true
}
}
}
now your view is up safeAreaLayoutGuide...but at the bottom you can see the tableview because there is no background (your view is up safeAreaLayoutGuide), for correct the problem I built a white uiview, presented it in inputTextField and set the constraint:
let dummyView = UIView()
dummyView.backgroundColor = .white
dummyView.translatesAutoresizingMaskIntoConstraints = false
Now set the constraint:
inputTextField.addSubview(dummyView)
dummyView.topAnchor.constraint(equalTo: inputTextField.bottomAnchor).isActive = true
dummyView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
dummyView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
dummyView.heightAnchor.constraint(equalToConstant: 50).isActive = true
This is the hack, but I think that's Xcode bug... I hope this help you...
I hope that for your code you simply add this bottom constraint for inputTextField and in other elements is needed, and set containerView CGRect frame height to 100:
self.inputTextField.bottomAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.bottomAnchor).isActive = true
and I suppose that you can delete:
self.inputTextField.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true

Resources