Aligning UIImageView to the left and right programmatically in Swift - swift

I'm working on a chat app with message bubbles. I've figured out how to get the messages conditionally formatted (color, size, etc.) based on who sent the message; however I'm having difficulty getting the chat bubbles to align to the left or right side of the view controller. Right now all items are aligned to the left. I've searched through SO and was unable to find an answer so I decided to ask.
UPDATED: Showing remaining code of setCell function which determines the format of the message based on the value of 'incoming'. Also include tableView function to show cell configuration. The last function is the setup function.
func setCell(cell: ConversationCell, incoming: [Int], indexPath: IndexPath) {
var layoutAttribute: NSLayoutAttribute
var layoutConstant: CGFloat
var smavalayoutConstant: CGFloat
for i in 0 ..< self.incoming.count {
if (self.incoming[indexPath.row] == 1) {
cell.bubbleImageView.image=#imageLiteral(resourceName: "chat_bubble_received")
cell.messageLbl.textColor = UIColor.black
cell.messageLbl.textAlignment = .left
cell.messageLbl?.numberOfLines = 0
cell.messageLbl?.lineBreakMode = .byWordWrapping
layoutAttribute = .left
layoutConstant = 0
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.bubbleImageView, attribute: .left, relatedBy: .equal, toItem: cell.contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.smavaImg, attribute: .left, relatedBy: .equal, toItem: cell.contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.postpictureImg, attribute: .left, relatedBy: .equal, toItem: cell.contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
}
if (self.incoming[indexPath.row] == 0) {
cell.bubbleImageView.image = #imageLiteral(resourceName: "chat_bubble_sent")
cell.messageLbl.textColor = UIColor.white
cell.messageLbl.textAlignment = .right
layoutAttribute = .right
layoutConstant = -100
smavalayoutConstant = 300
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.bubbleImageView, attribute: .leftMargin, relatedBy: .lessThanOrEqual, toItem: cell.contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.bubbleImageView, attribute: .right, relatedBy: .equal, toItem: cell.contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.smavaImg, attribute: .right, relatedBy: .equal, toItem: cell.smavaImg, attribute: layoutAttribute, multiplier: 1, constant: smavalayoutConstant))
cell.contentView.addConstraint(NSLayoutConstraint(item: cell.smavaImg, attribute: .left, relatedBy: .equal, toItem: cell.smavaImg, attribute: .left, multiplier: 1, constant: 300))
}
}
}
//cell config
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ConversationCell
// shortcuts
let hhpost = hhmessages[indexPath.row]
let image = avas[indexPath.row]
let smimages = images[indexPath.row]
let messagetext = hhpost["messagetext"] as? String
let date = hhpost["date"] as? String
cell.messageLbl.text = messagetext
cell.dateLbl.text = date
cell.smavaImg.image = image
cell.postpictureImg.image = smimages
DispatchQueue.main.async {
tableView.transform = CGAffineTransform(rotationAngle: -CGFloat.pi)
cell.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
self.setCell(cell: cell, incoming: self.incoming, indexPath: indexPath)
}
return cell
}
public func setup() {
bubbleImageView = UIImageView(image: #imageLiteral(resourceName: "chat_bubble_sent"), highlightedImage: #imageLiteral(resourceName: "chat_bubble_sent"))
bubbleImageView.isUserInteractionEnabled = true
messageLbl = UILabel(frame: CGRect.zero)
messageLbl.font = UIFont.systemFont(ofSize: 15)
messageLbl.numberOfLines = 0
messageLbl.isUserInteractionEnabled = true
selectionStyle = .none
contentView.addSubview(bubbleImageView)
contentView.addSubview(smavaImg)
bubbleImageView.addSubview(messageLbl)
messageLbl.translatesAutoresizingMaskIntoConstraints = false
bubbleImageView.translatesAutoresizingMaskIntoConstraints = false
contentView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: .left, relatedBy: .equal, toItem: contentView, attribute: .left, multiplier: 1, constant: 10))
contentView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .top, multiplier: 1, constant: 4.5))
bubbleImageView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: .width, relatedBy: .equal, toItem: messageLbl, attribute: .width, multiplier: 1, constant: 30))
contentView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottom, multiplier: 1, constant: -4.5))
bubbleImageView.addConstraint(NSLayoutConstraint(item: messageLbl, attribute: .centerX, relatedBy: .equal, toItem: bubbleImageView, attribute: .centerX, multiplier: 1, constant: -2))
bubbleImageView.addConstraint(NSLayoutConstraint(item: messageLbl, attribute: .centerY, relatedBy: .equal, toItem: bubbleImageView, attribute: .centerY, multiplier: 1, constant: -0.5))
messageLbl.preferredMaxLayoutWidth = 218
bubbleImageView.addConstraint(NSLayoutConstraint(item: messageLbl, attribute: .height, relatedBy: .equal, toItem: bubbleImageView, attribute: .height, multiplier: 1, constant: -15))
contentView.addConstraint(NSLayoutConstraint(item: smavaImg, attribute: .centerX, relatedBy: .equal, toItem: smavaImg, attribute: .centerX, multiplier: 1, constant: -2))
contentView.addConstraint(NSLayoutConstraint(item: smavaImg, attribute: .centerY, relatedBy: .equal, toItem: smavaImg, attribute: .centerY, multiplier: 1, constant: -0.5))
}

Take a look in my git hub project in your Playground
// Maximo Lucosi: https://github.com/lucosi/ChatTableView
You can create your tableViewCell with all constraints and change the constraints inside the cell.
Open the project in your Playgournd > View > Assistant Editor > Show Assistant Editor
class MyViewController: UIViewController {
// Messages for test
var messages: [Message] = []
// Table View here + basic configuration
lazy var tableView: UITableView = {
let view = UITableView()
view.translatesAutoresizingMaskIntoConstraints = false
view.delegate = self
view.dataSource = self
view.backgroundColor = .white
view.separatorStyle = .none
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
// Set the screen size befor implement layour. NOTICE: Only for playground test
self.view.frame = frame
// Messages for test //
self.messages.append(Message.init(message: "I'm working on a chat app with message bubbles.",
image: UIImage.init(),
incoming: 0))
self.messages.append(Message.init(message: "What is your dificulte.",
image: UIImage(),
incoming: 1))
self.messages.append(Message.init(message: "I'm having difficulty getting the chat bubbles to align to the left or right side of the view controller.",
image: UIImage.init(),
incoming: 0))
self.messages.append(Message.init(message: "One more for me",
image: UIImage(),
incoming: 1))
self.messages.append(Message.init(message: "I have already implemented a function that redraws UILabels with a passed ratio. So all I need to find is the text in UILabel from my view that would require the maximum ratio to redraw UILabels. So finally I need to do something like this:",
image: UIImage(), incoming: 1))
// End //
// Add the tableView
self.view.addSubview(tableView)
// Register teh cell in the tableView
self.tableView.register(ConversationCell.self, forCellReuseIdentifier: cellId)
// Criate a contraint for the table view
NSLayoutConstraint.activate(
[
tableView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 44),
tableView.leftAnchor.constraint(equalTo: self.view.leftAnchor),
tableView.rightAnchor.constraint(equalTo: self.view.rightAnchor),
tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
]
)
}
}
extension String {
//* Calculeta the hight string Function
func calculateTextFrameRect(
objectsInPlaceHeight: CGFloat,
objectsInPlaceWidth: CGFloat,
fontSize: CGFloat,
fontWeight: CGFloat) -> CGSize
{
let bounding = CGSize(width: UIScreen.main.bounds.width - objectsInPlaceWidth, height: .infinity)
let rect = NSString(string: self).boundingRect(
with: bounding,
options: NSStringDrawingOptions.usesFontLeading.union(NSStringDrawingOptions.usesLineFragmentOrigin),
attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: fontSize, weight: UIFont.Weight(rawValue: fontWeight))],
context: nil)
return CGSize(width: UIScreen.main.bounds.width, height: rect.height + objectsInPlaceHeight )
}
}
// Conform table view with delegate and data source
extension MyViewController: UITableViewDelegate, UITableViewDataSource {
// Change the hight of the cell
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// Calculate the hight of the cell
let text = self.messages[indexPath.item].message
let frameSize = text.calculateTextFrameRect(
objectsInPlaceHeight: 44 + 10,
objectsInPlaceWidth: 20 + 44 + 20 + self.view.frame.width * 0.4,
fontSize: UIFont.preferredFont(forTextStyle: UIFontTextStyle.body).pointSize,
fontWeight: UIFont.Weight.medium.rawValue)
return frameSize.height
}
// Number os cells
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.messages.count
}
// Return cell to display on the tableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! ConversationCell
cell.messageData = self.messages[indexPath.item]
return cell
}
}
//****** Custom cell class ******///
class ConversationCell: UITableViewCell {
var messageData: Message? {
didSet {
// Umrap the value incide the cell
if let message = messageData {
// Confiture the constraints for cell
if message.incoming == 0 {
// Text
self.messageTextView.textAlignment = .left
self.bubleImage.backgroundColor = .orange
// Constraints
self.lefBubleConstraint.isActive = true
self.rightBubleConstraint.isActive = false
} else {
// Text
self.messageTextView.textAlignment = .right
self.bubleImage.backgroundColor = .blue
// Constraints
self.lefBubleConstraint.isActive = false
self.rightBubleConstraint.isActive = true
}
if lefBubleConstraint.isActive == true {
self.leftMessageLable.isActive = true
self.rightMessageLable.isActive = false
} else {
self.leftMessageLable.isActive = false
self.rightMessageLable.isActive = true
}
// Set the data
self.messageTextView.text = message.message
self.bubleImage.image = message.image
}
}
}
// Create and config the image
let bubleImage: UIImageView = {
let view = UIImageView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .blue
view.layer.cornerRadius = 22
view.layer.masksToBounds = true
return view
}()
// Create and config the lable for the text
let messageTextView: UITextView = {
let view = UITextView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.lightGray.withAlphaComponent(0.5)
view.layer.cornerRadius = 10
view.textContainerInset = UIEdgeInsetsMake(5, 5, 5, 5)
view.isUserInteractionEnabled = false
return view
}()
// Constraints for configuration on didSet data
var lefBubleConstraint: NSLayoutConstraint!
var rightBubleConstraint: NSLayoutConstraint!
var leftMessageLable: NSLayoutConstraint!
var rightMessageLable: NSLayoutConstraint!
// Init the cell with local congiguration
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: nil)
self.addSubview(bubleImage)
self.addSubview(messageTextView)
// Permanent constraints
NSLayoutConstraint.activate(
[
self.bubleImage.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -10),
self.bubleImage.heightAnchor.constraint(equalToConstant: 44),
self.bubleImage.widthAnchor.constraint(equalToConstant: 44),
self.messageTextView.topAnchor.constraint(equalTo: self.topAnchor, constant: 10),
self.messageTextView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -10),
// NOTICE: Use the frame with as parameter for mesure the hight of cell on the main view
self.messageTextView.widthAnchor.constraint(equalToConstant: self.frame.width * 0.7)
]
)
// Buble constraint for configuration
self.lefBubleConstraint = self.bubleImage.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 10)
self.rightBubleConstraint = self.bubleImage.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -10)
// Message constrait for congiguration
self.rightMessageLable = self.messageTextView.rightAnchor.constraint(equalTo: self.bubleImage.leftAnchor, constant: -10)
self.leftMessageLable = self.messageTextView.leftAnchor.constraint(equalTo: self.bubleImage.rightAnchor, constant: 10)
}
// requerid init
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Related

Adding views dynamically to UIStackview

I'm trying to add views(or buttons) to UIStackView dynamically.
At first, the UIStackView has no arranged views (vertically), and
after getting from some http response, several views(buttons) are added to UIStackView.
UIStackView is also autolayout to hold a specific area.
I've tried to find dynamic adding example, but failed.
Anyone can show me the examples of adding view onto UIStackView dynamically?
It may help you. Please follow this points:
Add UIScrollView to your UIViewController in storyboard or XIB.
Initiate an NSMutableArray name it arrViews gets server response and adds view in the array.
Initialise UIStackViewpass arrView array in the init method.
After that UIStackView will be added subview of UIScrollView.
Add constraint programmatically to UIStackView. That's it.
if let response = self.serverResponse {
if let body = response.responseBody {
if let view = body.views {
arrViews = createSubViews(view)
}
}
}
let stackView = UIStackView(arrangedSubviews: arrViews)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.spacing = 16
stackView.distribution = .fill
self.scrollView.addSubview(stackView)
//constraints
let leading = NSLayoutConstraint(item: stackView, attribute: .leading, relatedBy: .equal, toItem: self.scrollView, attribute: .leading, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(leading)
let trailing = NSLayoutConstraint(item: stackView, attribute: .trailing, relatedBy: .equal, toItem: self.scrollView, attribute: .trailing, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(trailing)
let top = NSLayoutConstraint(item: stackView, attribute: .top, relatedBy: .equal, toItem: self.scrollView, attribute: .top, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(top)
let bottom = NSLayoutConstraint(item: stackView, attribute: .bottom, relatedBy: .equal, toItem: self.scrollView, attribute: .bottom, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(bottom)
let equalWidth = NSLayoutConstraint(item: stackView, attribute: .width, relatedBy: .equal, toItem: self.scrollView, attribute: .width, multiplier: 1.0, constant: 0)
self.scrollView.addConstraint(equalWidth)
leading.isActive = true
trailing.isActive = true
top.isActive = true
bottom.isActive = true
equalWidth.isActive = true
Hope it will help you. Happy coding :)
I use this code in one of my projects:
let baseFrame = CGRect(origin: .zero, size: CGSize(width: requiredWidth, height: partitionHeight))
for instrument in instruments {
let partitionView = PartitionOnDemand(instrument: instrument, mode: playbackMode, frame: baseFrame, referenceView: partitionsAnimator)
partitionsStackView.addArrangedSubview(partitionView)
let tab = InstrumentInfoTabContainer.instantiate(with: instrument) {
self.focus(on: instrument)
}
tabsStackView.addArrangedSubview(tab)
}
While trying with answers, I happend to find how to work it.
class ViewController: UIViewController {
#IBOutlet weak var stack: UIStackView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func onBtn_Create(_ sender: Any) {
createButton("new button ...")
}
#IBAction func onBtn_Delete(_ sender: Any) {
if let v = stack.arrangedSubviews.last {
stack.removeArrangedSubview(v)
v.removeFromSuperview()
}
}
func createButton(_ title: String) {
let button = UIButton()
button.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
button.backgroundColor = UIColor.blue
button.setTitle(title, for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
stack.addArrangedSubview(button)
}
#objc func buttonAction(sender: UIButton!) {
print("Button tapped")
}
}
And, I anchored to UIStackView, Trailing=0, Leading=0, Top=0, Bottom=8 to TextView.Top
The subviews inside it are intact without any constraints.
Thank you.

Centering label and UITextField inside a TableView

New to xcode/swift, spending a couple of days now trying to fix this one. Creating a universal app and having problems getting the constraint working programmatically. I would like to programmatically add a label and a UITextField inside a TableView. The label should always have a fixed width. The text field should have variable width depending on the device.
Here is what is looks like now:
Here is an idea of how it should look:
The label should be a set width. But the textfield should use the available screen.
Here is the code so far:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Setup Cell
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
// Make cell unselectable
cell.selectionStyle = .none
// Process Each Row
let row = indexPath.row
switch row
{
case 0:
let label = UILabel()
label.text = "First Name:"
label.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(40), height: CGFloat(30))
cell.contentView.addSubview(label)
var textField: UITextField = UITextField()
textField.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(170), height: CGFloat(30))
textField.borderStyle = .roundedRect
textField.backgroundColor = UIColor.magenta
textField.text = "TEST"
textField.textColor = UIColor.black
textField.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addSubview(textField)
let leadingConstraint = NSLayoutConstraint(item: cell.contentView, attribute: .leftMargin, relatedBy: .equal, toItem: label, attribute: .leftMargin, multiplier: 1.0, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: cell.contentView, attribute: .rightMargin, relatedBy: .equal, toItem: textField, attribute: .rightMargin, multiplier: 1.0, constant: 0)
cell.contentView.addConstraint(leadingConstraint)
cell.contentView.addConstraint(trailingConstraint)
....
Please let me know if you need additional information before a downvote. Any help would be appreciated. Thanks.
Answer by UpholderOfTruth:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Setup Cell
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
// Make cell unselectable
cell.selectionStyle = .none
// Process Each Row
let row = indexPath.row
switch row
{
case 0:
let label = UILabel()
label.text = "First Name:"
label.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(40), height: CGFloat(30))
label.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addSubview(label)
var textField: UITextField = UITextField()
textField.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(170), height: CGFloat(30))
textField.borderStyle = .roundedRect
textField.backgroundColor = UIColor.magenta
textField.text = "TEST"
textField.textColor = UIColor.black
textField.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addSubview(textField)
// Horizontal Constraints
cell.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[label(==100)][textField]|", options: .init(rawValue: 0), metrics: nil, views: ["label": label, "textField": textField]))
// Vertical Constraints
cell.contentView.addConstraint(NSLayoutConstraint(item: label, attribute: .centerY, relatedBy: .equal, toItem: cell.contentView, attribute: .centerY, multiplier: 1, constant: 0))
cell.contentView.addConstraint(NSLayoutConstraint(item: textField, attribute: .centerY, relatedBy: .equal, toItem: cell.contentView, attribute: .centerY, multiplier: 1, constant: 0))
....
I would suggest go full auto layout and don't mix methods. So first set both view to use auto layout via setting the translatesAutoresizingMaskIntoConstraints to false.
Then either set the constraints visually like this:
cell.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[label(==100)][textField]|", options: .init(rawValue: 0), metrics: nil, views: ["label": label, "textField": textField]))
or with individual constraints like this:
cell.contentView.addConstraint(NSLayoutConstraint(item: label, attribute: .left, relatedBy: .equal, toItem: cell, attribute: .left, multiplier: 1, constant: 0))
cell.contentView.addConstraint(NSLayoutConstraint(item: label, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: 100))
cell.contentView.addConstraint(NSLayoutConstraint(item: label, attribute: .right, relatedBy: .equal, toItem: textField, attribute: .left, multiplier: 1, constant: 0))
cell.contentView.addConstraint(NSLayoutConstraint(item: textField, attribute: .right, relatedBy: .equal, toItem: cell, attribute: .right, multiplier: 1, constant: 0))
Of course this just handles horiztonal positioning and sizing you need to do something about vertical positioning and sizing as well but you may be setting that up further down in your code.
Edit:
To centre vertically you can do this:
cell.contentView.addConstraint(NSLayoutConstraint(item: label, attribute: .centerY, relatedBy: .equal, toItem: cell.contentView, attribute: .centerY, multiplier: 1, constant: 0))
cell.contentView.addConstraint(NSLayoutConstraint(item: textField, attribute: .centerY, relatedBy: .equal, toItem: cell.contentView, attribute: .centerY, multiplier: 1, constant: 0))
Edit2:
Combining into a single line:
cell.contentView.addConstraints([NSLayoutConstraint(item: label, attribute: .centerY, relatedBy: .equal, toItem: cell.contentView, attribute: .centerY, multiplier: 1, constant: 0), NSLayoutConstraint(item: textField, attribute: .centerY, relatedBy: .equal, toItem: cell.contentView, attribute: .centerY, multiplier: 1, constant: 0)])
Edit3:
cell.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-8-[label(==100)][textField]-8-|", options: .init(rawValue: 0), metrics: nil, views: ["label": label, "textField": textField]))
Change these properties:
switch row
{
case 0:
let leadingConstraint = NSLayoutConstraint(item: cell.contentView, attribute: .leftMargin, relatedBy: .equal, toItem: label, attribute: .leftMargin, multiplier: 1.0, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: cell.contentView, attribute: .rightMargin, relatedBy: .equal, toItem: textField, attribute: .rightMargin, multiplier: 1.0, constant: 0)
cell.contentView.addConstraint(leadingConstraint)
cell.contentView.addConstraint(trailingConstraint)
let label = UILabel()
label.text = "First Name:"
//here is the trick: play with x and width. It might also be cell.contentView.size().width
label.frame = CGRect(x: CGFloat(35), y: CGFloat(0), width: CGFloat(cell.frame.width * 1/3), height: CGFloat(30))
cell.contentView.addSubview(label)
var textField = UITextField()
textField.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(cell.frame.width * 2/3), height: CGFloat(30))
textField.borderStyle = .roundedRect
textField.backgroundColor = UIColor.magenta
textField.text = "TEST TEST TEST"
textField.textColor = UIColor.black
textField.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addSubview(textField)
Let me know if this works.

UICollectionViewFlowLayout issues with collectionview and textfields

I have an app that uses a collectionview that scrolls horizontally, and has collectionviewcells inside of it. Everything was going fine until I tried to implement a login/register cell with 2 textfields in it using textfielddelegate. When I press on one of the textfields, the keyboard shows for a split second and then hides. After it does this, the view is pushed down a little bit and if I press the textfield again, it is pushed up and wont come down. Here are some screenshots of what it looks like:
before touching a textfield vs. after I touch a textfield
I get various UICollectionViewFlowLayout errors that call for me to make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes.
The behavior of the UICollectionViewFlowLayout is not defined because:
2017-04-26 13:55:03.199199-0400 Eyetube[1500:243622] the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values.
2017-04-26 13:55:03.200490-0400 Eyetube[1500:243622] The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x10453ee90>, and it is attached to <UICollectionView: 0x104806800; frame = (0 0; 768 960); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x17405a610>; layer = <CALayer: 0x17002f0a0>; contentOffset: {2304, 0}; contentSize: {3072, 910}> collection view layout: <UICollectionViewFlowLayout: 0x10453ee90>.
2017-04-26 13:55:03.200575-0400 Eyetube[1500:243622] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.
I tried debugging this multiple times, but it isn't too helpful in finding where exactly the error is coming from. I've been stuck on this issue for hours and can't seem to figure it out.
Where I initialize my layout in my collectionviewcontroller:
func setupCollectionView() {
if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.scrollDirection = .horizontal
flowLayout.minimumLineSpacing = 0
}
collectionView?.backgroundColor = UIColor.white
collectionView?.register(VideoFeedCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.register(ChannelFeedCell.self, forCellWithReuseIdentifier: channelCellId)
collectionView?.register(ARFeedCell.self, forCellWithReuseIdentifier: augmentedRealityCellId)
collectionView?.register(LoginRegisterCell.self, forCellWithReuseIdentifier: loginRegisterCellId)
collectionView?.contentInset = UIEdgeInsetsMake(0, 0, 50, 0)
collectionView?.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, 50, 0)
collectionView?.isPagingEnabled = true
}
My sizeForItemAt func, also in my collectionviewcontroller (P.S., I am subtracting 50 from the height because of the bottom menubar I have added as a subview of the view. I also changed the collectionview's contentInset and scrollIndicatorInsets because of this):
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cellSize = CGSize(width: view.frame.width, height: view.frame.height - 50)
return cellSize
}
Here is the complete code of the collectionviewcell, where I am having the issues:
import UIKit
class LoginRegisterCell: BaseCell, UITextFieldDelegate {
let logoImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "eyetube_logo_font")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
return imageView
}()
let emailTextField: LeftPaddedTextField = {
let tf = LeftPaddedTextField()
tf.keyboardType = .emailAddress
tf.placeholder = "Enter email"
tf.substituteFontName = "SourceSansPro-Regular"
tf.layer.borderColor = UIColor.rgb(220, green: 220, blue: 220).cgColor
tf.layer.borderWidth = 1
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
let passwordTextField: LeftPaddedTextField = {
let tf = LeftPaddedTextField()
tf.placeholder = "Enter password"
tf.substituteFontName = "SourceSansPro-Regular"
tf.layer.borderColor = UIColor.rgb(220, green: 220, blue: 220).cgColor
tf.layer.borderWidth = 1
tf.isSecureTextEntry = true
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
lazy var loginButton: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = UIColor.rgb(225, green: 31, blue: 40)
button.titleLabel?.font = UIFont(name: "SourceSansPro-SemiBold", size: 20)
button.setTitle("Log In", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(handleLogin), for: .touchUpInside)
return button
}()
lazy var registerLink: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Don't have an account? Register here", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.titleLabel?.font = UIFont(name: "SourceSansPro-Regular", size: 18)
button.setTitleColor(UIColor.darkGray, for: .normal)
let underlineAttribute = [NSUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue]
let underlineAttributedString = NSAttributedString(string: (button.titleLabel?.text)!, attributes: underlineAttribute)
button.titleLabel?.attributedText = underlineAttributedString
button.addTarget(self, action: #selector(handleRegister), for: .touchUpInside)
return button
}()
var containerView: UIView!
override func setupViews() {
super.setupViews()
self.emailTextField.delegate = self
emailTextField.returnKeyType = .next
self.passwordTextField.delegate = self
passwordTextField.returnKeyType = .done
containerView = UIView(frame: CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height))
containerView.backgroundColor = .green
addSubview(containerView)
containerView.addSubview(logoImageView)
containerView.addSubview(emailTextField)
containerView.addSubview(passwordTextField)
containerView.addSubview(loginButton)
containerView.addSubview(registerLink)
setupLogoImageView()
setupInputs()
setupLoginButton()
setupRegisterLink()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func handleLogin() {
//first check if the email/password textfields are empty or not
guard let email = emailTextField.text , !email.isEmpty else {
let alert = UIAlertView (title: "Invalid Email", message: "Please enter an email to log in", delegate: self, cancelButtonTitle: "OK")
alert.show()
return
}
guard let password = passwordTextField.text , !password.isEmpty else {
let alert = UIAlertView (title: "Invalid Password", message: "Please enter a password to log in", delegate: self, cancelButtonTitle: "OK")
alert.show()
return
}
//create session here
ApiLoginAuthentication.sharedInstance.login_now(username: email, password: password, onCompletion: {(loginSuccessful: Bool) -> Void in
guard (loginSuccessful) else {
DispatchQueue.main.async {
let alert = UIAlertView (title: "Invalid Account info", message: "The account information entered is invalid. Please log in with a valid account.", delegate: self, cancelButtonTitle: "OK")
alert.show()
}
return
}
DispatchQueue.main.async {
self.emailTextField.text = ""
self.passwordTextField.text = ""
}
print("the login was successful")
})
/**
let profileUrl: String = "https://eyetube.net/user/profile.asp"
ApiLoginAuthentication.sharedInstance.getContent(contentUrl: profileUrl, onCompletion: {(responseString: String, isLoggedIn: Bool) -> Void in
print(responseString)
print("user status: \(isLoggedIn)")
let json: Any?
do {
let data: NSData = responseString.data(using: String.Encoding.utf8)! as NSData
json = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments)
print(json)
} catch let error {
print("error: \(error)")
}
})**/
}
func handleRegister() {
let eyetubeRegisterLink = "https://eyetube.net/user/register.asp?rUrl="
UIApplication.shared.openURL(URL(string: eyetubeRegisterLink)!)
}
func setupLogoImageView() {
var sizeConstant: CGFloat!
var centerYConstant: CGFloat!
if UI_USER_INTERFACE_IDIOM() == .pad {
sizeConstant = 400
centerYConstant = -260
} else {
sizeConstant = 200
centerYConstant = -160
}
NSLayoutConstraint(item: logoImageView, attribute: .centerY, relatedBy: .equal, toItem: containerView, attribute: .centerY, multiplier: 1, constant: centerYConstant!).isActive = true
NSLayoutConstraint(item: logoImageView, attribute: .centerX, relatedBy: .equal, toItem: containerView, attribute: .centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: logoImageView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: sizeConstant).isActive = true
NSLayoutConstraint(item: logoImageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: sizeConstant).isActive = true
}
func setupInputs() {
var widthConstant: CGFloat!
var leftConstant: CGFloat!
if UI_USER_INTERFACE_IDIOM() == .pad {
widthConstant = -64
leftConstant = 32
} else {
widthConstant = -32
leftConstant = 16
}
NSLayoutConstraint(item: emailTextField, attribute: .top, relatedBy: .equal, toItem: logoImageView, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: emailTextField, attribute: .left, relatedBy: .equal, toItem: containerView, attribute: .left, multiplier: 1, constant: leftConstant).isActive = true
NSLayoutConstraint(item: emailTextField, attribute: .width, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: 1, constant: widthConstant).isActive = true
NSLayoutConstraint(item: emailTextField, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 50).isActive = true
NSLayoutConstraint(item: passwordTextField, attribute: .top, relatedBy: .equal, toItem: emailTextField, attribute: .bottom, multiplier: 1, constant: 8).isActive = true
NSLayoutConstraint(item: passwordTextField, attribute: .left, relatedBy: .equal, toItem: containerView, attribute: .left, multiplier: 1, constant: leftConstant).isActive = true
NSLayoutConstraint(item: passwordTextField, attribute: .width, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: 1, constant: widthConstant).isActive = true
NSLayoutConstraint(item: passwordTextField, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 50).isActive = true
}
func setupLoginButton() {
NSLayoutConstraint(item: loginButton, attribute: .centerX, relatedBy: .equal, toItem: containerView, attribute: .centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: loginButton, attribute: .top, relatedBy: .equal, toItem: passwordTextField, attribute: .bottom, multiplier: 1, constant: 16).isActive = true
NSLayoutConstraint(item: loginButton, attribute: .width, relatedBy: .equal, toItem: passwordTextField, attribute: .width, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: loginButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 50).isActive = true
}
func setupRegisterLink() {
NSLayoutConstraint(item: registerLink, attribute: .centerX, relatedBy: .equal, toItem: containerView, attribute: .centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: registerLink, attribute: .top, relatedBy: .equal, toItem: loginButton, attribute: .bottom, multiplier: 1, constant: 12).isActive = true
NSLayoutConstraint(item: registerLink, attribute: .width, relatedBy: .equal, toItem: loginButton, attribute: .width, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: registerLink, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 40).isActive = true
}
}

How do I send subviews to front programmatically? (have tried bringSubview to front)

In my UICollectionViewCell I have an image and a label. The picture takes up the whole cell (which I want) - however, the label is placed behind the image, so it's not visible. I have tried bringSubview(toFront: titleLabel), but nothing happens... I got no clue what to do really, have done a lot of searching.
This is the code for the cell, I don't use Storyboard as you can see (sorry for messy constraints, was testing different solutions to find out if this was the problem)
import UIKit
class BaseCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
setupBasket()
}
func setupViews() {
}
func setupBasket(){
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class VideoCell: BaseCell {
var selectedItemID : String!
static let sharedInstance = VideoCell()
var video: Video? {
didSet {
titleLabel.text = video?.title
setupThumbnailImage()
}
}
func setupThumbnailImage() {
if let thumbnailImageUrl = video?.thumbnail_image_name {
thumbnailImageView.loadImageUsingUrlString(thumbnailImageUrl)
}
}
let thumbnailImageView: CustomImageView = {
let imageView = CustomImageView()
imageView.image = UIImage(named: "taylor_swift_blank_space")
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
let titleLabel: UILabel = {
let textView = UILabel()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.text = "Clothes"
textView.textColor = UIColor.lightGray
return textView
}()
let separatorView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(red: 230/255, green: 230/255, blue: 230/255, alpha: 1)
return view
}()
var titleLabelHeightConstraint: NSLayoutConstraint?
let addtoBasket = UIButton(type: .contactAdd)
override func setupViews() {
addtoBasket.frame = CGRect(x: 50, y: 0, width: 20, height: 60)
addSubview(addtoBasket)
addSubview(titleLabel)
addSubview(thumbnailImageView)
addSubview(separatorView)
addSubview(addtoBasket)
titleLabel.superview!.bringSubview(toFront: titleLabel)
//horizontal constraints
addConstraintsWithFormat("H:|-0-[v0]-0-|", views: thumbnailImageView)
//vertical constraints
addConstraintsWithFormat("V:|-1-[v0]-1-|", views: thumbnailImageView)
addConstraintsWithFormat("H:|-0-[v0]-1-|", views: separatorView)
addtoBasket.translatesAutoresizingMaskIntoConstraints = false
addtoBasket.heightAnchor.constraint(equalToConstant: 20).isActive = true
addtoBasket.widthAnchor.constraint(equalToConstant: 20).isActive = true
addtoBasket.centerXAnchor.constraint(equalTo: addtoBasket.superview!.centerXAnchor, constant: 90).isActive = true
addtoBasket.centerYAnchor.constraint(equalTo: addtoBasket.superview!.centerYAnchor, constant: -50).isActive = true
//top constraint
addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .top, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 8))
//right constraint
addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1, constant: 0))
//right constraint
addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1, constant: 20))
//height constraint
titleLabelHeightConstraint = NSLayoutConstraint(item: titleLabel, attribute: .height, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 1, constant: -10)
addConstraint(titleLabelHeightConstraint!)
}
}
Try accessing the labels layer and set its zPosition.
Try titleLabel.layer.zPosition = 1
There was clearly something wrong with the constraints, now working! Thanks

Autolayout constraints set in code not appearing in interface builder

I have a custom view that is set in interface builder with top, leading, trailing, height constraints.
In my Custom view i have a title and a button.
Im trying to add to the title a bottom and centerY constraints.
and to the button width, height, bottom, leading constraints.
When i add any constraint i get an warning in interface builder:
Expected: width=600, height=68.
Actual: width=0, height=0
When i run the code everything works, but i cant see anything in interface builder.
code:
#IBDesignable
class UIHeader: UIView {
var delegate: HeaderDelegate?
private lazy var titleLable: UILabel = {
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.font = UIFont(name: "Lato-Light", size: 16)
lbl.text = "Title"
return lbl
}()
private lazy var backButton: UIButton = {
let btn = UIButton()
btn.tintColor = UIColor.lightGrayColor()
btn.translatesAutoresizingMaskIntoConstraints = false
let image = UIImage(named: "prev")
if let image = image {
btn.setImage(image.imageWithRenderingMode(.AlwaysTemplate), forState: .Normal)
}
btn.addTarget(self, action: #selector(UIHeader.OnBackButtonClickLister(_:)), forControlEvents: .TouchUpInside)
return btn
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
}
extension UIHeader {
#IBInspectable
var backButtonImage: UIImage? {
get {
return backButton.imageForState(.Normal)
}
set (newImage) {
backButton.setImage(newImage?.imageWithRenderingMode(.AlwaysTemplate), forState: .Normal)
}
}
#IBInspectable
var title: String? {
get {
return titleLable.text
}
set (newTitle) {
titleLable.text = newTitle
}
}
}
extension UIHeader {
private func setupView() {
backgroundColor = UIColor.whiteColor()
translatesAutoresizingMaskIntoConstraints = false
addSubview(titleLable)
addSubview(backButton)
//add shadow
layer.shadowColor = UIColor(white: 115/255, alpha: 1.0).CGColor
layer.shadowOpacity = 0.5
layer.shadowRadius = 8
layer.shadowOffset = CGSizeMake(0, -1)
NSLayoutConstraint.activateConstraints([
//Title//
//center x
NSLayoutConstraint(item: titleLable, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .CenterX, multiplier: 1.0, constant: 0),
//bottom
NSLayoutConstraint(item: self, attribute: .Bottom, relatedBy: .Equal, toItem: titleLable, attribute: .Bottom, multiplier: 1, constant: 12),
//button//
//bottom
NSLayoutConstraint(item: self, attribute: .Bottom, relatedBy: .Equal, toItem: backButton, attribute: .Bottom, multiplier: 1, constant: 4),
//leading
NSLayoutConstraint(item: backButton, attribute: .Leading, relatedBy: .Equal, toItem: self, attribute: .Leading, multiplier: 1, constant: 0),
//width
NSLayoutConstraint(item: backButton, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .Width, multiplier: 1, constant: 40),
//height
NSLayoutConstraint(item: backButton, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .Height, multiplier: 1, constant: 40)
])
}
}
I also tried to add the constraints with:
addConstraint(NSLayoutConstraint)
cant figure out what is the problem.
Thanks
I removed
translatesAutoresizingMaskIntoConstraints = false
and everything works great.