class ChatCollectionViewCell: UICollectionViewCell {
var chatView: UIView!
var chatTextView: UITextView!
var isTextFromCurrentUser: Bool = true
var chatViewWidth: CGFloat = 200
override init(frame: CGRect) {
super.init(frame: frame)
chatView = UIView()
chatTextView = UITextView()
contentView.addSubview(chatView)
contentView.addSubview(chatTextView)
setupViews()
}
override func layoutSubviews() {
if isTextFromCurrentUser {
chatView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 10).isActive = true
chatTextView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 15).isActive = true
chatTextView.backgroundColor = .white
} else {
chatView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -10).isActive = true
chatTextView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -15).isActive = true
chatTextView.backgroundColor = UIColor(r: 157, g: 255, b: 164)
}
chatView.widthAnchor.constraint(equalToConstant: chatViewWidth).isActive = true
chatView.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
chatTextView.widthAnchor.constraint(equalTo: chatView.widthAnchor, constant: -10).isActive = true
chatTextView.topAnchor.constraint(equalTo: chatView.topAnchor, constant: 5).isActive = true
chatView.translatesAutoresizingMaskIntoConstraints = false
chatTextView.translatesAutoresizingMaskIntoConstraints = false
}
func setupViews() {
chatView.backgroundColor = .blue
chatTextView.font = UIFont.systemFont(ofSize: 16)
chatTextView.layer.cornerRadius = 9
chatTextView.clipsToBounds = true
chatTextView.isScrollEnabled = false
}
override func prepareForReuse() {
super.prepareForReuse()
chatView = nil
chatTextView = nil
chatView = UIView()
chatTextView = UITextView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
(for clarification, I am setting the chatViewWidth and the chatTextView.text property in the ViewController's cellForRow method)
So right now, the error that XCode is giving is the following: "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?"
I looked through many posts regarding common ancestor errors on StackOverflow, but none of the solutions solved my problem.
Which is very confusing.
I tried using breakpoints to analyze what was wrong, but the program crashed after creating around ~14 cells or so. And sometimes it works, but when I add more cells, it will begin crashing. I'm not sure what the issue is--my views are definitely children views of the CollectionViewCell, correct?
Thank you!
This is how i would do it ... well basicy i would set all the stuff in Storyboard and set an Outlet for the widthConstraint ... but to take your code, this should work. But its not testet ... :)
class ChatCollectionViewCell: UICollectionViewCell {
var chatView: UIView!
var chatTextView: UITextView!
var isTextFromCurrentUser: Bool = true {
didSet {
if isTextFromCurrentUser {
NSLayoutConstraint.deactivate(rightAlignmentConstraints)
NSLayoutConstraint.activate(leftAlignmentConstraints)
chatTextView.backgroundColor = .white
} else {
NSLayoutConstraint.deactivate(leftAlignmentConstraints)
NSLayoutConstraint.activate(rightAlignmentConstraints)
chatTextView.backgroundColor = UIColor(r: 157, g: 255, b: 164)
}
}
}
var chatViewWidth: CGFloat = 200 {
didSet {
chatView.widthAnchor.constraint(equalToConstant: chatViewWidth).isActive = true
}
}
private var leftAlignmentConstraints: [NSLayoutConstraint] = []
private var rightAlignmentConstraints: [NSLayoutConstraint] = []
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func setupViews() {
chatView = UIView()
chatTextView = UITextView()
contentView.addSubview(chatView)
contentView.addSubview(chatTextView)
chatView.translatesAutoresizingMaskIntoConstraints = false
chatTextView.translatesAutoresizingMaskIntoConstraints = false
chatView.backgroundColor = .blue
chatTextView.font = UIFont.systemFont(ofSize: 16)
chatTextView.layer.cornerRadius = 9
chatTextView.clipsToBounds = true
chatTextView.isScrollEnabled = false
leftAlignmentConstraints = [
chatView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 10),
chatTextView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 15)
]
rightAlignmentConstraints = [
chatView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -10),
chatTextView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -15)
]
chatView.widthAnchor.constraint(equalToConstant: chatViewWidth).isActive = true
chatView.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
chatTextView.widthAnchor.constraint(equalTo: chatView.widthAnchor, constant: -10).isActive = true
chatTextView.topAnchor.constraint(equalTo: chatView.topAnchor, constant: 5).isActive = true
NSLayoutConstraint.activate(leftAlignmentConstraints)
}
override func prepareForReuse() {
super.prepareForReuse()
chatTextView.text = ""
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Good Luck and Happy Coding
Related
I have a subclass of UIButton and it have an initialiser that accept a name and boolean. I have a function to toggle the hide and show imageView, and my auto layout set to when imageView hidden the anchor move into another imageView. I use the content hugging priority programmatically in this. so here is my code, can you show me why my uiimageView not hiding.
// This is in my subclass of UIButton
let profileLbl = UILabel()
let badgeImageView = UIImageView()
let rightArrowImageView = UIImageView()
var isHiddenBadge = false
var visibleProfileTrailingConstraint: NSLayoutConstraint!
var hiddenProfileTrailingConstraint: NSLayoutConstraint!
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white
configure()
}
init(name: String, isBadgeHidden: Bool = false) {
super.init(frame: .zero)
profileLbl.text = name
profileLbl.font = UIFont(name: "NunitoSans-SemiBold", size: 16)
profileLbl.textColor = #colorLiteral(red: 0.2, green: 0.2, blue: 0.2, alpha: 1)
isHiddenBadge = isBadgeHidden
toggleHide(badge: isHiddenBadge)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configure() {
translatesAutoresizingMaskIntoConstraints = false
[profileLbl, badgeImageView, rightArrowImageView].forEach({ v in
v.translatesAutoresizingMaskIntoConstraints = false
addSubview(v)
})
visibleProfileTrailingConstraint = profileLbl.trailingAnchor.constraint(equalTo: badgeImageView.leadingAnchor, constant: -5)
hiddenProfileTrailingConstraint = profileLbl.trailingAnchor.constraint(equalTo: rightArrowImageView.leadingAnchor, constant: -5)
visibleProfileTrailingConstraint.priority = .defaultHigh
hiddenProfileTrailingConstraint.priority = .defaultLow
badgeImageView.image = #imageLiteral(resourceName: "warning_error 1")
rightArrowImageView.image = #imageLiteral(resourceName: "ic-arrow-right")
NSLayoutConstraint.activate([
profileLbl.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 24),
profileLbl.centerYAnchor.constraint(equalTo: centerYAnchor),
visibleProfileTrailingConstraint,
hiddenProfileTrailingConstraint,
badgeImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
badgeImageView.widthAnchor.constraint(equalToConstant: 24),
badgeImageView.heightAnchor.constraint(equalToConstant: 24),
rightArrowImageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -24),
rightArrowImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
rightArrowImageView.widthAnchor.constraint(equalToConstant: 16),
rightArrowImageView.heightAnchor.constraint(equalToConstant: 16)
])
}
private func toggleHide(badge: Bool) {
if badge == false {
if badgeImageView.isHidden {
badgeImageView.isHidden = false
visibleProfileTrailingConstraint.priority = .defaultHigh
hiddenProfileTrailingConstraint.priority = .defaultLow
}
} else {
visibleProfileTrailingConstraint.priority = .defaultLow
hiddenProfileTrailingConstraint.priority = .defaultHigh
badgeImageView.isHidden = true
}
}
// I initialise it in my viewController
let infoBtn = GTProfileBtn(name: "Basic Info", isBadgeHidden: false)
// this is when I try to test it in my viewDidLoad
infoBtn.isHiddenBadge = true
Use following
var isHiddenBadge = false {
didSet {
toggleHide(badge: isHiddenBadge)
}
}
The problem is you not calling toggleHide after setting isHiddenBadge. The above code will solve the issue.
Any idea why the Tap gesture on a UI Label will not fire?
I've tried using a delegate also but in its simplest form, for some reason it will just not hit the action method.
Is the UIView layer restricting this interaction?
class TestTextViewLabel : UIView {
weak var testTextView: UITextView!
weak var testLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(testLabelTapped(_:)))
let testUITextView: UITextView = {
let textView = UITextView()
textView.textColor = UIColor(hex: "#000")
textView.translatesAutoresizingMaskIntoConstraints = false
return textView
}()
let testUILabel: UILabel = {
let label = UILabel()
label.textColor = UIColor(hex: "#666666")!
label.translatesAutoresizingMaskIntoConstraints = false
label.addGestureRecognizer(tapGesture)
label.isUserInteractionEnabled = true
return label
}()
self.addSubview(testUITextView)
self.addSubview(testUILabel)
self.testTextView = testUITextView
self.testLabel = testUILabel
testUITextView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
testUITextView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
testUITextView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
testUILabel.topAnchor.constraint(equalTo: testUITextView.bottomAnchor, constant: 50).isActive = true
testUILabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
testUILabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func testLabelTapped(_ sender: UITapGestureRecognizer) {
print("testLabelTapped")
}
}
I tried running your class and I was able to get the UILabel to fire after putting text into it. The tap gesture is only recognized within the view's bounds and since you didn't have any text in the UILabel, the bounds were zero giving you no place to click it. By default, if you put in text, the UILabel will automatically match those bounds. Here is my working code below:
class TestTextViewLabel : UIView {
weak var testTextView: UITextView!
weak var testLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(testLabelTapped(_:)))
let testUITextView: UITextView = {
let textView = UITextView()
textView.textColor = UIColor.black
textView.translatesAutoresizingMaskIntoConstraints = false
textView.text = "This is a text view"
textView.backgroundColor = .clear
return textView
}()
let testUILabel: UILabel = {
let label = UILabel()
label.textColor = UIColor(red:0.40, green:0.40, blue:0.40, alpha:1.0)
label.translatesAutoresizingMaskIntoConstraints = false
label.addGestureRecognizer(tapGesture)
label.isUserInteractionEnabled = true
label.text = "This is a UILabel view"
return label
}()
self.addSubview(testUITextView)
self.addSubview(testUILabel)
self.testTextView = testUITextView
self.testLabel = testUILabel
testUITextView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
testUITextView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
testUITextView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
testUILabel.topAnchor.constraint(equalTo: testUITextView.bottomAnchor, constant: 50).isActive = true
testUILabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
testUILabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func testLabelTapped(_ sender: UITapGestureRecognizer) {
print("testLabelTapped")
}
}
Introduction
I'm creating an app which uses a custom view in which I have a UIStackView to sort out 5 UIControls. When a user taps one of the UIControls an underscore line gets animated, sliding under the tapped UIControl.
However, for some reason the method/selector for these UIControls no longer gets called. I believe this has to do with that I updated my Mac to the macOS (and Xcode) update released this week (wk.44). (updated from swift 4.2 to swift 4.2.1). Before the updated this animation and selector worked perfectly. But I'm not sure. And I'm now completely stuck on what I'm doing wrong.
Context
I created a playground and scaled down everything as much as I could and the issue persists.
I have tried to define the UIStackView in the global scope of my SetupView class but it doesn't change anything. So I believe it is not an issue of the stackView or its subviews being deallocated?
Below I've provided my UIControl subclass and my SetupView (UIView subclass) that I use. I've created a playground so you may copy paste in Xcode playground to test if you want.
Question
Why doesn't the method goalViewControlTapped(_ sender: SetupViewControl) get called?
Code
import UIKit
import PlaygroundSupport
class SetupViewControl: UIControl {
let titleLabel : UILabel = {
let lbl = UILabel()
lbl.font = UIFont(name: "Futura", size: 14)
lbl.textColor = .white
lbl.backgroundColor = .clear
lbl.textAlignment = .center
lbl.translatesAutoresizingMaskIntoConstraints = false
return lbl
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupLabel()
layer.cornerRadius = 5
}
fileprivate func setupLabel() {
addSubview(titleLabel)
titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5).isActive = true
titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var isHighlighted: Bool {
didSet {
UIView.animate(withDuration: 0.12) {
self.backgroundColor = self.isHighlighted ? UIColor.lightGray : UIColor.clear
}
}
}
}
class SetupView: UIView {
let dataModel : [String] = ["2 weeks", "1 month", "2 months", "6 months", "1 year"]
var selectionLineCenterX : NSLayoutConstraint!
let selectionLine = UIView()
let labelZero = SetupViewControl()
let labelOne = SetupViewControl()
let labelTwo = SetupViewControl()
let labelThree = SetupViewControl()
let labelFour = SetupViewControl()
let labelFive = SetupViewControl()
lazy var controlArray = [self.labelZero, self.labelOne, self.labelTwo, self.labelThree, self.labelFour, self.labelFive]
init(frame: CGRect, color: UIColor) {
super.init(frame: frame)
self.backgroundColor = color
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
fileprivate func setupView() {
layer.cornerRadius = 0
layer.borderColor = UIColor.black.cgColor
layer.borderWidth = 1
setupLabelText()
setupControlsInStackView()
}
fileprivate func setupLabelText() {
for num in 0...(dataModel.count - 1) {
controlArray[num].titleLabel.text = dataModel[num]
}
}
// let stackView = UIStackView(frame: .zero) I have tried to declare the stackView here but it doesn't fix my issue.
func setupControlsInStackView() {
var stackViewArray = [SetupViewControl]()
for num in 0...(dataModel.count - 1) {
controlArray[num].isUserInteractionEnabled = true
controlArray[num].addTarget(self, action: #selector(goalViewControlTapped(_:)), for: .touchUpInside)
stackViewArray.append(controlArray[num])
}
let stackView = UIStackView(arrangedSubviews: stackViewArray)
stackView.alignment = .fill
stackView.distribution = .fillEqually
stackView.axis = .horizontal
stackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8).isActive = true
stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8).isActive = true
stackView.topAnchor.constraint(equalTo: topAnchor, constant: 15).isActive = true
addSubview(selectionLine)
selectionLine.backgroundColor = .white
selectionLine.translatesAutoresizingMaskIntoConstraints = false
selectionLine.heightAnchor.constraint(equalToConstant: 1).isActive = true
selectionLine.topAnchor.constraint(equalTo: stackView.bottomAnchor).isActive = true
selectionLine.widthAnchor.constraint(equalToConstant: 50).isActive = true
selectionLineCenterX = selectionLine.centerXAnchor.constraint(equalTo: leadingAnchor, constant: -100)
selectionLineCenterX.isActive = true
}
#objc fileprivate func goalViewControlTapped(_ sender: SetupViewControl) {
print("This is not getting printed!!!")
selectionLineCenterX.isActive = false
selectionLineCenterX = selectionLine.centerXAnchor.constraint(equalTo: sender.centerXAnchor)
selectionLineCenterX.isActive = true
UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.5, options: .curveEaseIn, animations: {
self.layoutIfNeeded()
}, completion: nil)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let testView = SetupView(frame: .zero, color: UIColor.blue)
view.addSubview(testView)
testView.translatesAutoresizingMaskIntoConstraints = false
testView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
testView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
testView.heightAnchor.constraint(equalToConstant: 100).isActive = true
testView.widthAnchor.constraint(equalToConstant: 365).isActive = true
}
}
// For live view in playground
let vc = ViewController()
vc.preferredContentSize = CGSize(width: 375, height: 812)
PlaygroundPage.current.liveView = vc
Thanks for reading my question.
Does your UIStackView show as having an ambiguous layout when you open the view debugger? If so, that may be causing the internal views to not receive the touch events.
You can provide UIStackView with either:
x and y constraints only
or
x, y, width and height.
In the above case the height constraint is missing.
Here is my code
import UIKit
class PageCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
let imageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.backgroundColor = .yellow
iv.image = UIImage(named: "page1")
return iv
}()
func setupViews() {
backgroundColor = .blue
addSubview(imageView)
imageView.(topAnchor, bottomAnchor, rightAnchor, leftAnchor)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
When compiling this it shows this error. Value of type 'UIImageView' has no member 'anchorToTop' By the way I am trying to anchor an image to an individual UIImageViewCell.
You can try
func setupViews() {
backgroundColor = .blue
imageView.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(imageView)
imageView.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 0 ).isActive = true
imageView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: 0 ).isActive = true
imageView.leftAnchor.constraint(equalTo: self.contentView.leftAnchor, constant: 0 ).isActive = true
imageView.rightAnchor.constraint(equalTo: self.contentView.rightAnchor, constant: 0 ).isActive = true
}
I'm working on a exercise to create a custom control of anything. My idea is to have a UIView in the middle of the screen and a UILabel below it. When you tap on the view a random color will appear with the label changing to its hex value. When trying to create this custom control I'm having a problem trying to center the UIView programmatically. I get to an issue at `colorBox.center~
import UIKit
#IBDesignable
class Color: UIView {
private lazy var label : UILabel = {
let label = UILabel()
label.backgroundColor = UIColor.clear
label.translatesAutoresizingMaskIntoConstraints = false
label.heightAnchor.constraint(equalToConstant: 25.0).isActive = true
label.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
label.font = .systemFont(ofSize: 15.0, weight: UIFontWeightRegular)
return label
}()
private lazy var colorGen : UIView = {
let colorBox = UIView()
colorBox.backgroundColor = UIColor.black
colorBox.heightAnchor.constraint(equalToConstant: 100.0).isActive = true
colorBox.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
colorBox.centerXAnchor.constraint(equalTo: colorBox.frame.size.width /2)
}()
override init (frame: CGRect) {
super.init(frame:frame)
setUpLabel()
setUpView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUpLabel()
setUpView()
}
I've tried the answers about using self.view but it doesn't work for me so I'm a bit lost.
You're close, but you need to add the label and the view so you can then constrain them relative to the superview...
#IBDesignable
class ColorView: UIView {
private lazy var colorLabel : UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = UIColor.clear
label.heightAnchor.constraint(equalToConstant: 25.0).isActive = true
label.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
label.font = .systemFont(ofSize: 15.0, weight: UIFontWeightRegular)
return label
}()
private lazy var colorGen : UIView = {
let colorBox = UIView()
colorBox.translatesAutoresizingMaskIntoConstraints = false
colorBox.backgroundColor = UIColor.cyan
colorBox.heightAnchor.constraint(equalToConstant: 100.0).isActive = true
colorBox.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
return colorBox
}()
override init (frame: CGRect) {
super.init(frame:frame)
commonSetup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonSetup()
}
func commonSetup() -> Void {
self.addSubview(colorGen)
self.addSubview(colorLabel)
colorGen.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0.0).isActive = true
colorGen.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0.0).isActive = true
colorGen.topAnchor.constraint(equalTo: self.topAnchor, constant: 0.0).isActive = true
colorLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0.0).isActive = true
colorLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0.0).isActive = true
colorLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0.0).isActive = true
colorLabel.topAnchor.constraint(equalTo: colorGen.bottomAnchor, constant: 0.0).isActive = true
colorLabel.text = "the label"
}
}