Adding UIButtons dynamically in UICollectionViewCell - Swift - swift

Trying to show buttons dynamically in a UICollectionViewCell depends on data coming in array from api. Below is what trying to achieve:
This is so far what i have achieved using button, but i need multiple buttons means button next to "About us" then next until space there then move to next line and setting height of cell as per height of view in cell. Please guide how it can be achieved.
Below is my code:
var xPosition = cell.btnOptions.frame.origin.x
var yPosition = cell.btnOptions.frame.origin.y
var width = cell.btnOptions.frame.size.width
var height = cell.btnOptions.frame.size.height
if((arrOptions?.count ?? 0) > 0)
{
for i in 0..<arrOptions!.count
{
//
cell.btnOptions.tag = i
cell.btnOptions.layer.cornerRadius = 3.0
cell.btnOptions.frame = CGRect.init(x: xPosition, y: yPosition, width: width, height: height)
//
cell.btnOptions.setTitle(arrOptions?.objectAt(i).object(forKey: "title") as? String, for: .normal)
xPosition = width + width + 8
yPosition = height
}

Related

How can I get frame of superview's frame in swift?

I want to create multiple buttons and position it inside uiview and fit to uiview.(as picture)
I need to get uiview frame to calculate and divide as I need , to set button's width and height depending on device size.
for row in 0 ..< 4 {
for col in 0..<3 {
let numberButton = UIButton()
numberButton.frame = CGRect(x: Int(buttonView.frame.width / 3 - 20) * col, y: row * (320 / 4), width: Int(buttonView.frame.width) / 3, height: Int(buttonView.frame.height) / 4)
numberButton.setTitle("button", for: .normal)
numberButton.titleLabel?.font = numberButton.titleLabel?.font.withSize(30)
numberButton.setTitleColor(UIColor.black)
buttonView.addSubview(numberButton)
}
}
I tried like code above, but buttonView.frame.width returns nil.
How can I calculate this view's frame?
You can use UIStackViews to achieve this grid layout. This way, you don't have to calculate the frames of each button. Doing so is bad practice anyway. You should instead use AutoLayout constraints to layout your views. Here's a tutorial to get you started.
Anyway, here's how you would use UIStackViews to create a grid of buttons:
// here I hardcoded the frame of the button view, but in reality you should add
// AutoLayout constraints to it to specify its frame
let buttonView = UIStackView(frame: CGRect(x: 0, y: 0, width: 600, height: 320))
buttonView.alignment = .fill
buttonView.axis = .vertical
buttonView.distribution = .fillEqually
buttonView.spacing = 20 // this is the spacing between each row of buttons
for _ in 0..<4 {
var buttons = [UIButton]()
for _ in 0..<3 {
let numberButton = UIButton(type: .system)
numberButton.setTitle("button", for: .normal)
numberButton.titleLabel?.font = numberButton.titleLabel?.font.withSize(30)
numberButton.setTitleColor(UIColor.black, for: .normal)
// customise your button more if you want...
buttons.append(numberButton)
}
let horizontalStackView = UIStackView(arrangedSubviews: buttons)
horizontalStackView.alignment = .fill
horizontalStackView.axis = .horizontal
horizontalStackView.distribution = .fillEqually
horizontalStackView.spacing = 20 // this is the spacing between each column of buttons
buttonView.addArrangedSubview(horizontalStackView)
}
Result from playground quick look:

How to add space between buttons in uiscrollview

I was trying so hard to add space between buttons array in UIScrollView but I couldn't make it. There is any way to make space between buttons array in UIScrollView ?
I have attached a picture explaining what I want.
This is my code :
//MARK: - SETUP CATEGORIES BUTTONS
func setupCategoriesButtons() {
var xCoord: CGFloat = 1
let yCoord: CGFloat = 1
let buttonHeight: CGFloat = 40
let buttonwidth: CGFloat = 40
let gap: CGFloat = 0
// Counter for items
var itemCount = 0
// Loop for creating buttons
for i in 0..<myArray.count {
itemCount = i
// Create a Button
let catButt = UIButton(type: .custom)
catButt.showsTouchWhenHighlighted = true
catButt.setTitle("\(myArray[itemCount])", for: .normal)
catButt.sizeToFit()
catButt.frame = CGRect(x: xCoord, y: yCoord, width: catButt.frame.size.width + 30, height: buttonHeight)
catButt.layer.cornerRadius = 8
catButt.titleLabel?.font = UIFont(name: "Titillium-Semibold", size: 12)
// catButt.addTarget(self, action: #selector(categoryButt(_:)), for: UIControlEvents.touchUpInside)
// Add Buttons & Labels based on xCood
xCoord += catButt.frame.size.width + gap
categoriesScrollView.addSubview(catButt)
letterButtons.append(catButt)
} // END LOOP --------------------------
for i in 0..<letterButtons.count {
letterButtons[i].tag = i
letterButtons[i].backgroundColor = RandomColor()
letterButtons[i].setTitleColor(UIColor.white, for: .normal)
}
// Place Buttons into the ScrollView
categoriesScrollView.contentSize = CGSize(width: buttonwidth * CGFloat(itemCount+1), height: yCoord)
}
let gap: CGFloat = 0
Looks wrong, if you want a gap. Shouldn't it be e.g. ... = 15?
Alternatives could include placing them in a UIStackView with a spacing set. This would also be simple using auto-layout, creating constraints between the buttons which provide the gaps.
Another suggestion:
I would suggest to use a UIStackView for achieving the desired appearance instead of adding the buttons directly in the scroll view. The gap would be represented as spacing property.
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
// here is the "gap":
stackView.spacing = 16.0
therefore, you should add the buttons in it:
stackView.addArrangedSubview(button)

Issue with custom UICollectionViewLayout leaving gap at bottom of cell

I have a UICollectionView with a custom layout. As seen in the image below there is always a gap between the content and the bottom of the cell.
Each cell consists of an image and three labels. I've printed out the size of the of the cell and also printed out the size of the image + the 3 lables and the match, so I have no idea where this extra space is coming from.
let width = columnWidth - cellPadding*2
let photoHeight = delegate.collectionView(collectionView!, heightForPhotoAtIndexPath: indexPath , withWidth:width)
print("photo height is: \(photoHeight)")//photo height is: 95.4078125
let label1Height = delegate.collectionView(collectionView!, heightForLabel1AtIndexPath: indexPath, withWidth: width)
print("label1Height is \(label1Height)") // label1Height is 39.0
let label2Height = delegate.collectionView(collectionView!,heightForLabel2AtIndexPath: indexPath, withWidth: width) //label2Height is 53.0
print("label2Height is \(label2Height)")// label2Height is 53.0
let label3Height = delegate.collectionView(collectionView!, heightForLabel3AtIndexPath: indexPath, withWidth: width)
print("label3Height is \(label3Height)") //label3Height is 135.0
let height = photoHeight + label1Height + label2Height + label3Height
print("total height is: \(height)") //total height is: 322.4078125
let frame = CGRect(x: xOffset[column], y: yOffset[column], width: columnWidth, height: height)
let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
I've pinned the bottom of label3 to the bottom of the cell +4, but that doesn't help

UITableview with dynamic height cells & filling footer view

I am trying to make a UITableview with a footer view that fills the remaining empty space of the UITableview. However, the catch is that the cells have varying heights.
I am aware that, if the cells were all a static height, I could simply count how many cells there are and multiply that by the static height. Unfortunately, that won't work in this case.
Current solution:
Works for static height cells.
Does not work for dynamic height cells.
private func adjustFooterHeight() {
if data.count == 0 {
footerView.frame.size.height = tableView.bounds.height
}
else {
//I use data.count instead of (data.count - 1) as I have the extra hidden cell, as pictured in image 2.
let bottomFrame = tableView.rectForRow(at: IndexPath(row: data.count, section: 0))
let height = tableView.bounds.height - (bottomFrame.origin.y + bottomFrame.height - rowHeight)
footerView.frame.size.height = height < 0 ? 0 : height
tableView.reloadData()
}
}
Diagram of Intentions:
So I figured it out...
Here is the solution.
private func adjustFooterHeight() {
//Get content height & calculate new footer height.
let cells = self.tableView.visibleCells
var height: CGFloat = 0
for i in 0..<cells.count {
height += cells[i].frame.height
}
height = self.tableView.bounds.height - ceil(height)
//If the footer's new height is negative, we make it 0, since we don't need footer anymore.
height = height > 0 ? height : 0
//Create the footer
let footerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: height))
self.tableView.tableFooterView = footerView
}

Create UILabel at TopLeft of UIVIew and animate straight downwards?

Iv been working on this new game for a couple months now and run into a problems I cant figure out!
So basically I am creating a "status section" in the top right of my UIViewController. In this "status section" will appear UILabels that hold status updates as Strings. These UILabels will appear at the very top left of the UIView and will go slowly downwards and lose Alpha at the same time..eventually dissappearing once it reaches the bottom.
What Iv Done?
So as you can see in the code before so far Iv been able to create the UILabel at random locations but for some reason the UILabels spawn off screen.
Code
func createStatus() {
let viewWidth = self.statusView.frame.width
let viewHeight = self.statusView.frame.width
let randomX = CGFloat(viewWidth / 2)
let randomY = CGFloat(arc4random_uniform(UInt32(viewHeight)) + 50)
let frame = self.statusView.frame.origin
let status = UILabel(frame: CGRect(origin: CGPoint(x: CGFloat(viewWidth / 4) - 20, y: CGFloat(viewHeight - viewHeight) + 10), size: CGSize(width: 200.0, height: 50.0)))
status.text = "This is a status"
status.textColor = UIColor.redColor()
status.alpha = 1
self.statusView.addSubview(status)
print("Status made")
UIView.animateWithDuration(2) {
status.center = frame
status.alpha = 0
}
}
Iv added an image of the problem and the UIViewstatusView as well. Im having trouble making the UILabel at the top left and i'v tried this code below and it doesnt even "spawn" the UILabel on the screen.
let frame = self.statusView.frame.origin
CGPoint topLeft = CGPointMake(CGRectGetMinX(frame), CGRectGetMinY(frame));
func createStatus() {
let viewWidth = self.statusView.frame.width
let viewHeight = self.statusView.frame.width
//Why are these here?
let randomX = CGFloat(viewWidth / 2)
let randomY = CGFloat(arc4random_uniform(UInt32(viewHeight)) + 50)
let origin = CGPointZero//Origins are relative to superview, so top left would be (0, 0)
//Renamed status to statusLabel so that we know its a label and not a model object
let statusLabel = UILabel(frame: CGRect(origin: origin, size: CGSize(width: 200.0, height: 50.0)))
statusLabel.text = "This is a status"
statusLabel.textColor = UIColor.redColor()
statusLabel.alpha = 1
self.statusView.addSubview(statusLabel)
print("Status made")
UIView.animateWithDuration(2) {
//Animate the origin instead of center position.
//The origin will be the height of the statusView - the height of your label. x stays 0 to keep it along left edge.
statusLabel.frame.origin = CGPoint(x: 0, y: self.statusView.frame.height - statusLabel.frame.height )
statusLabel.alpha = 0
}, { (completed) in
if completed {
//If statusLabel doesn't have a superView it will be dealloc after the function goes out of scope. So remove it from superview after animaion is complete and ARC takes care of it.
statusLabel.removeFromSuperview()
}
}
}