Swift - Linking programmatically created buttons and labels to each other - swift

I am creating everything programmatically and I am having issues linking multiple buttons to react to a selected custom action.
the blue button is created programmatically and I use tags to keep track which one is pressed. When the blue button is selected, an action menu pops up which can link actions to the button by pressing the add icon.
You can select the action you want by clicking on "select" then press and drag from the "o" button to create the connector.
I store in a dictionary the selected action and which buttons are connected to it
The issue is how I can link the "o" button, "select" button and "name" label from the same row to each other when they are created programmatically? I am not using tableview to create the actions. Would that be easier to use?
This create the action row
// MARK: - ACTION Input
func createAction()
{
let actionTabContainer = UIView()
actionTabContainer.frame = CGRect(x: 0, y: 0, width: 0, height: 0)
actionTabContainer.translatesAutoresizingMaskIntoConstraints = false
actionTabContainer.backgroundColor = UIColor.darkGray
actionTabContainer.layer.borderWidth = 2
actionTabContainer.layer.borderColor = UIColor(red: 29/255.0, green: 30/255.0, blue: 33/255.0, alpha: 1.0).cgColor
actionScrollViewContainer.addSubview(actionTabContainer)
actionTabContainer.widthAnchor.constraint(equalToConstant: actionScrollViewContainer.frame.width).isActive = true
actionTabContainer.heightAnchor.constraint(equalToConstant: 50).isActive = true
actionTabContainer.leftAnchor.constraint(equalTo: actionScrollViewContainer.leftAnchor, constant: 10).isActive = true
actionTabContainer.topAnchor.constraint(equalTo: actionScrollViewContainer.topAnchor, constant: 2 + constantAdd).isActive = true
constantAdd = constantAdd + 50
let connectorBtn = UIButton()
connectorBtn.createRectangleButton(buttonPositionX: 0, buttonPositionY: 0, buttonWidth: 0, buttonHeight: 0, buttonTitle: "O", buttonTag: 400)
connectorBtn.translatesAutoresizingMaskIntoConstraints = false
connectorBtn.backgroundColor = UIColor.gray
actionTabContainer.addSubview(connectorBtn)
connectorBtn.widthAnchor.constraint(equalToConstant: 30).isActive = true
connectorBtn.heightAnchor.constraint(equalToConstant: 30).isActive = true
connectorBtn.leftAnchor.constraint(equalTo: actionTabContainer.leftAnchor, constant: 10).isActive = true
connectorBtn.centerYAnchor.constraint(equalTo: actionTabContainer.centerYAnchor, constant: 0).isActive = true
connectorBtn.addTarget(self, action: #selector(addConnector(sender:)), for: .touchUpInside)
addPanReconiser(view: connectorBtn)
let chooseActionButton = UIButton()
chooseActionButton.createRectangleButton(buttonPositionX: 0, buttonPositionY: 0, buttonWidth: 0, buttonHeight: 0, buttonTitle: "select", buttonTag: 700)
chooseActionButton.translatesAutoresizingMaskIntoConstraints = false
chooseActionButton.backgroundColor = UIColor.gray
chooseActionButton.layer.cornerRadius = 0
actionTabContainer.addSubview(chooseActionButton)
chooseActionButton.widthAnchor.constraint(equalToConstant: 110).isActive = true
chooseActionButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
chooseActionButton.leftAnchor.constraint(equalTo: connectorBtn.rightAnchor, constant: 10).isActive = true
chooseActionButton.centerYAnchor.constraint(equalTo: connectorBtn.centerYAnchor, constant: 0).isActive = true
chooseActionButton.addTarget(self, action: #selector(addConnector(sender:)), for: .touchUpInside)
let actionMarkerConnectedLabel = UILabel()
actionMarkerConnectedLabel.createLabel(labelPositionX: 0, labelPositionY: 0, labelWidth: 0, labelHeight: 0, labelTitle: "name")
actionMarkerConnectedLabel.backgroundColor = UIColor.gray
actionMarkerConnectedLabel.translatesAutoresizingMaskIntoConstraints = false
actionMarkerConnectedLabel.textAlignment = .center
connectorBtn.addSubview(actionMarkerConnectedLabel)
actionMarkerConnectedLabel.widthAnchor.constraint(equalToConstant: 100).isActive = true
actionMarkerConnectedLabel.heightAnchor.constraint(equalToConstant: 32).isActive = true
actionMarkerConnectedLabel.leftAnchor.constraint(equalTo: chooseActionButton.rightAnchor, constant: 10).isActive = true
actionMarkerConnectedLabel.centerYAnchor.constraint(equalTo: connectorBtn.centerYAnchor, constant: 0).isActive = true
}
I have my own extensions to create rectangles and other shapes which might look confusing.
Thanks for any recommendations

I think it will better if using table view and control them by indexPath. If not, you can create a variable to keep "Count" then when create new :
count += 1
button1.tag = count
button2.tag = count

Related

How to change size and color of image UIButton?

I have the UIButton that I create, like this
let btn = UIButton()
view.addSubview(btn)
btn.setImage(UIImage(systemName: "star"), for: .normal)
btn.translatesAutoresizingMaskIntoConstraints = false
btn.frame = CGRect(x: 30, y: 30, width: 150, height: 150)
btn.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 35).isActive = true
btn.rightAnchor.constraint(equalTo: btnDelete.leftAnchor, constant: 0).isActive = true
btn.heightAnchor.constraint(equalToConstant: 44).isActive = true
btn.widthAnchor.constraint(equalToConstant: 44).isActive = true
the problem is that I see the star icon button, but I can't find a way to make it bigger and change the color of the star itself, currently it is blue, but I need it white.
For make it bigger use below code to fill width and height u giving in btn.frame :
btn.contentHorizontalAlignment = .fill
btn.contentVerticalAlignment = .fill
For make it change the color of icon use :
btn.tintColor = .white

Swift - TableViewCell - Layout margin not working

I have a table view which should display comments with their replies. Whenever the replies are shown, there should be a margin on the left side so it is clear that a particular reply belongs to the comment above.
To achieve that, I tried following:
class CommentsTableViewCell: UITableViewCell {
// label containing name of comment writer
private func initSenderLabel() {
senderLabel.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(senderLabel)
senderLabel.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 18).isActive = true
senderLabel.widthAnchor.constraint(equalToConstant: 150).isActive = true
senderLabel.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 6).isActive = true
senderLabel.heightAnchor.constraint(equalToConstant:14).isActive = true
senderLabel.font = .boldSystemFont(ofSize: 14)
senderLabel.adjustsFontSizeToFitWidth = true
senderLabel.textColor = .gray
}
// actual comment
private func initCommentLabel() {
commentLabel.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(commentLabel)
commentLabel.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 18).isActive = true
commentLabel.widthAnchor.constraint(equalToConstant: 209).isActive = true
commentLabel.topAnchor.constraint(equalTo: self.senderLabel.bottomAnchor, constant: 2).isActive = true
commentLabel.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -29).isActive = true
commentLabel.font = .boldSystemFont(ofSize: 16)
commentLabel.textColor = .black
commentLabel.lineBreakMode = .byWordWrapping
commentLabel.numberOfLines = 0
}
// button to load replies
private func initLoadReplyButton() {
loadReplyButton.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(loadReplyButton)
loadReplyButton.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 50).isActive = true
loadReplyButton.widthAnchor.constraint(equalToConstant: 150).isActive = true
loadReplyButton.topAnchor.constraint(equalTo: commentLabel.bottomAnchor, constant: 0).isActive = true
loadReplyButton.heightAnchor.constraint(equalToConstant: 24).isActive = true
loadReplyButton.addTarget(self, action: #selector(loadReplyButton_touched), for: .touchDown)
loadReplyButton.setTitle(NSLocalizedString(LOAD_REPLIES_BUTTON_TITLE, comment: ""), for: .normal)
loadReplyButton.backgroundColor = .clear
loadReplyButton.setTitleColor(.darkGray, for: .normal)
}
// Sets up the cell using the comment data
func setup(comment: Comment, sender:String) {
// Assign comment
self.commentObject = comment
// Update UI depending if it is a comment or reply
if comment is CommentReply {
loadReplyButton.isHidden = true
contentView.layoutMargins = UIEdgeInsets(top: 0, left: 150, bottom: 0, right: 0)
}
else {
// Remove insets
loadReplyButton.isHidden = false
UIView.setAnimationsEnabled(false)
updateButtonTitle()
UIView.setAnimationsEnabled(true)
}
if !comment.hasReplies {
loadReplyButton.isHidden = true
}
// Update size layout
self.layoutIfNeeded()
}
However, the code above still shows me the replies as it does for the "normal" comments. How can I modify the position of the replies?

Create background color change animation

I am trying to create an animation where I change the color of a rectangle from orange to blue. Here is where I created the rectangle and set its background color to orange:
let colorView = UIView(frame: CGRect(x: 250, y: 500, width: 75, height: 75))
colorView.backgroundColor = UIColor.orange
and here is where I add the animation with the function definition of the animation below it
let colorChangeAnimation = constructColorChangeAnimation()
colorView.layer.add(colorChangeAnimation, forKey: "color")
private func constructColorChangeAnimation() -> CABasicAnimation {
let ColorChangeAnimation = CABasicAnimation(keyPath: "backgroundColor")
ColorChangeAnimation.fromValue = UIColor.orange
ColorChangeAnimation.toValue = UIColor.blue
return ColorChangeAnimation
}
This implementation doesn't seem to work and I'm not sure what it is I need to change. The documentation says that the fromValue and toValue should be the way to change the animation but it just doesn't seem to want to work. (also fyi I did originally have a duration variable for the animation function but it also wasn't working then)
you should use UIColor.orange.cgColor
animation.fromValue = UIColor.orange.cgColor
animation.duration = 3
and UIView animation is much easier
colorView.backgroundColor = UIColor.orange
UIView.animate(withDuration: 3) {
self.aview.backgroundColor = UIColor.blue
}
Try this one;
let colourAnim = CABasicAnimation(keyPath: "backgroundColor")
colourAnim.fromValue = self.colorView.backgroundColor
colourAnim.toValue = UIColor.blue.cgColor
colourAnim.duration = 1.0
self.colorView.addAnimation(colourAnim, forKey: "colourAnimation")
self.colorView.backgroundColor = UIColor.blue.cgColor
You should set the background color of the view after the animation and use o .cgColor
Use UIView.animate like this:
Under your class controller declare view and button (in my example I add corner radius for more cute result)
let chamgeButton = UIButton(type: .system)
let orangeView = UIView()
in viewDidLoad construct them, present and add constraints:
orangeView.backgroundColor = .orange
orangeView.layer.cornerRadius = 8
orangeView.clipsToBounds = true
orangeView.translatesAutoresizingMaskIntoConstraints = false
chamgeButton.backgroundColor = .red
chamgeButton.setTitle("changeColor", for: .normal)
chamgeButton.setTitleColor(.white, for: .normal)
chamgeButton.layer.cornerRadius = 8
chamgeButton.clipsToBounds = true
chamgeButton.addTarget(self, action: #selector(handleChangeColor), for: .touchUpInside)
chamgeButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(orangeView)
orangeView.heightAnchor.constraint(equalToConstant: 200).isActive = true
orangeView.widthAnchor.constraint(equalToConstant: 200).isActive = true
orangeView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
orangeView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
view.addSubview(chamgeButton)
chamgeButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
chamgeButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
chamgeButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
chamgeButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
now add the target button function to change color:
#objc fileprivate func handleChangeColor() {
UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseOut, animations: {
self.orangeView.backgroundColor = .blue
self.view.layoutIfNeeded()
}, completion: nil)
}
This is the result:

Cannot see Buttons in UIScrollView

I was making a list in the form of scrollview in swift where the view consists of various types such as labels, button etc.
However when i added the button to the subview, they were not displayed although all other labels etc were displayed. I also tried messing around in the constraints and anchors.
On the other hand when i added the same button to self.view.addsubview instead of scrollview.addsubview, they were displayed just not scrolling since not a part of the scrollview anymore.
I even removed the label to make sure that the buttons were not being overlapped(didn't work either)
I also tried to see the code in "code debug hierarchy " (3D mode), i couldn't see the button there either even though i had added it
Below is my code with an example of label, scrollview and button. It be great if anyone could provide any insights.....thanks either way....
................scrollview..........................
var editInfoView : UIScrollView = {
let view = UIScrollView()
view.translatesAutoresizingMaskIntoConstraints = false
view.contentSize.height = 700
view.backgroundColor = tableBackGroundColor
view.frame = CGRect(x: 0, y: 220, width: 375, height: 400)
return view
}()
.......................label...................
vehicleNumberLabel.translatesAutoresizingMaskIntoConstraints = false
vehicleNumberLabel.textColor = .white
vehicleNumberLabel.text = "Vehicle Number"
vehicleNumberLabel.textAlignment = .left
editInfoView.addSubview(vehicleNumberLabel)
vehicleNumberLabel.leftAnchor.constraint(equalTo: editInfoView.leftAnchor).isActive = true
vehicleNumberLabel.topAnchor.constraint(equalTo: editInfoView.topAnchor, constant: 100).isActive = true
vehicleNumberLabel.widthAnchor.constraint(equalToConstant: 160).isActive = true
vehicleNumberLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true
.....................button................................
vehicleNumberButton.translatesAutoresizingMaskIntoConstraints = false
vehicleNumberButton.setTitleColor(tableTextColor, for: .normal)
vehicleNumberButton.setTitle("Vehicle Number", for: .normal)
vehicleNumberButton.tintColor = tableTextColor
vehicleNumberButton.backgroundColor = tableTextColor
editInfoView.addSubview(vehicleNumberButton)
vehicleNumberButton.rightAnchor.constraint(equalTo: editInfoView.rightAnchor).isActive = true
vehicleNumberButton.topAnchor.constraint(equalTo: editInfoView.topAnchor, constant: 400).isActive = true
vehicleNumberButton.widthAnchor.constraint(equalToConstant: 600).isActive = true
vehicleNumberButton.heightAnchor.constraint(equalToConstant: 255).isActive = true
Although I cannot determine the root cause of your issue with the code and explanation provided I suspect the frame of your UIScrollView() is zero after viewDidAppear(_:) adding subviews to a CGRect.zero can cause some strange behavior with the layout engine. When we create constraints programmatically we are creating a combination of inequalities, equalities, and priorities to restrict the view to a particular frame. If a the value of these constraint equations is incorrect it changes how your relating views appear. Its good practice to avoid the use of leftAnchor and rightAnchor as well, because views may flip direction based on language (writing direction) and user settings.
ViewController.swift
import UIKit
class ViewController: UIViewController {
var editInfoScrollView : UIScrollView = {
let view = UIScrollView()
view.translatesAutoresizingMaskIntoConstraints = false
view.isUserInteractionEnabled = true
view.alwaysBounceVertical = true
view.isScrollEnabled = true
view.contentSize.height = 700
view.backgroundColor = UIColor.red.withAlphaComponent(0.3)
// Does nothing because `translatesAutoresizingMaskIntoConstraints = false`
// Instead, set the content size after activating constraints in viewDidAppear
//view.frame = CGRect(x: 0, y: 220, width: 375, height: 400)
return view
}()
var vehicleNumberLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.black
label.text = "Vehicle Number"
label.textAlignment = .left
return label
}()
lazy var vehicleNumberButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.tag = 1
button.setTitleColor(UIColor.black, for: .normal)
button.setTitle("Go to Vehicle", for: .normal)
button.tintColor = UIColor.white
button.backgroundColor = UIColor.clear
button.layer.cornerRadius = 30 // about half of button.frame.height
button.layer.borderColor = UIColor.black.cgColor
button.layer.borderWidth = 2.0
button.layer.masksToBounds = true
button.addTarget(self, action: #selector(handelButtons(_:)), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
self.setupSubviews()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.editInfoScrollView.contentSize = CGSize(width: self.view.frame.width, height: 700.0)
}
func setupSubviews() {
self.view.addSubview(editInfoScrollView)
editInfoScrollView.addSubview(vehicleNumberLabel)
editInfoScrollView.addSubview(vehicleNumberButton)
let spacing: CGFloat = 12.0
let constraints:[NSLayoutConstraint] = [
editInfoScrollView.widthAnchor.constraint(equalTo: self.view.widthAnchor),
editInfoScrollView.heightAnchor.constraint(equalToConstant: 400.0),
editInfoScrollView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
editInfoScrollView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 220.0),
vehicleNumberLabel.leadingAnchor.constraint(equalTo: editInfoScrollView.leadingAnchor, constant: spacing),
vehicleNumberLabel.trailingAnchor.constraint(equalTo: editInfoScrollView.trailingAnchor, constant: -spacing),
vehicleNumberLabel.centerXAnchor.constraint(equalTo: editInfoScrollView.centerXAnchor, constant: -50),
vehicleNumberLabel.heightAnchor.constraint(equalToConstant: 75.0),
vehicleNumberButton.widthAnchor.constraint(equalTo: editInfoScrollView.widthAnchor, multiplier: 0.66),
vehicleNumberButton.heightAnchor.constraint(equalToConstant: 65.0),
vehicleNumberButton.topAnchor.constraint(equalTo: vehicleNumberLabel.bottomAnchor, constant: spacing),
vehicleNumberButton.centerXAnchor.constraint(equalTo: editInfoScrollView.centerXAnchor),
]
NSLayoutConstraint.activate(constraints)
}
#objc func handelButtons(_ sender: UIButton) {
switch sender.tag {
case 0:
print("Default button tag")
case 1:
print("vehicleNumberButton was tapped")
default:
print("Nothing here yet")
}
}
}

Swift 3 - Constrain Button Width

I am wanting to create a simple bar at the top of my view, with two buttons side by side, taking up 50% each.
I have created the bar like so:
let topTabView = UIView()
topTabView.backgroundColor = UIColor.red
topTabView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(topTabView)
topTabView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
topTabView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
topTabView.heightAnchor.constraint(equalToConstant: 60).isActive = true
topTabView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
And this works.
I then add my two buttons, and I get all the constraints right, except I'm not sure how to get the width anchor to work, so that each button takes up 50% of the view.
let filterButton = UIButton()
filterButton.backgroundColor = UIColor.red
filterButton.setTitle("Filter", for: .normal)
filterButton.translatesAutoresizingMaskIntoConstraints = false
topTabView.addSubview(filterButton)
filterButton.leftAnchor.constraint(equalTo: topTabView.leftAnchor).isActive = true
filterButton.centerYAnchor.constraint(equalTo: topTabView.centerYAnchor).isActive = true
filterButton.heightAnchor.constraint(equalTo: topTabView.heightAnchor).isActive = true
// NOT SURE ABOUT THIS ONE
filterButton.widthAnchor.constraint(equalToConstant: 200).isActive = true
let mapButton = UIButton()
mapButton.backgroundColor = UIColor.red
mapButton.setTitle("Map", for: .normal)
mapButton.translatesAutoresizingMaskIntoConstraints = false
topTabView.addSubview(mapButton)
mapButton.rightAnchor.constraint(equalTo: topTabView.rightAnchor).isActive = true
mapButton.centerYAnchor.constraint(equalTo: topTabView.centerYAnchor).isActive = true
mapButton.heightAnchor.constraint(equalTo: topTabView.heightAnchor).isActive = true
// NOT SURE ABOUT THIS ONE
mapButton.widthAnchor.constraint(equalToConstant: 200).isActive = true
I tried something like this but it didn't work:
mapButton.widthAnchor.constraint(equalToConstant: toTabView.frame.width / 2.0).isActive = true
Any help would be amazing!
so that each button takes up 50% of the view
That is what the multiplier is for. You aren't using it. Use it!
So, you want a constraint where a button's width is the same as the superview's width except with a 0.5 value for its multiplier.
Example:
let b1 = UIButton()
b1.translatesAutoresizingMaskIntoConstraints = false
b1.backgroundColor = .green
b1.setTitle("Button1", for: .normal)
b1.setTitleColor(.black, for: .normal)
let b2 = UIButton()
b2.translatesAutoresizingMaskIntoConstraints = false
b2.backgroundColor = .yellow
b2.setTitle("Button2", for: .normal)
b2.setTitleColor(.black, for: .normal)
self.view.addSubview(b1)
self.view.addSubview(b2)
NSLayoutConstraint.activate([
b1.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 50),
b2.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 50),
b1.leadingAnchor.constraint(equalTo:self.view.leadingAnchor),
b2.leadingAnchor.constraint(equalTo:b1.trailingAnchor),
b1.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.5),
b2.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.5),
])
Result: