Generate imageView based off array.count - Swift - arrays

I am trying to generate a section of image views which will sit at the top of an application and update as the user progresses through the quiz.
My array variable is Qs, the code I have to generate an imageView is as follows:
var imageView: UIImageView!
var i = 0
var total = Int(Qs.capacity) // Just testing what .capacity does.
for i in 0..<(Qs.count-1){
imageView - UIImageView(frame: CGRect(x: 0, y: 75, width: 50, height: 50))
imageView.image = UIImage(named:"InfoIcon")
imageView.contentMode = .scaleAspectFit
self.view.addSubView(imageView)
}
I already have a variable which tracks the users progress through the quiz with just an integer if that would be any help, its declared along with the quiz functionality.
Here is a fantastically draw visual of what I am trying to accomplish:
Any help is appreciated,
thanks

Lets assume you have a container view attached at the top, then you can use this method to add any number of imageView's.
func addProgressImages(to view: UIView, count: Int) {
let screenSize = UIScreen.main.bounds.size
for i in 0..<count {
let spacing: CGFloat = 2
let tag = i + 888
let width: CGFloat = screenSize.width/CGFloat(count)
let height: CGFloat = width
if let view = view.viewWithTag(tag) {
view.removeFromSuperview()
}
let x = spacing + CGFloat(i) * width
let imageView = UIImageView(frame: CGRect(x: x, y: 100, width: width - spacing, height: height))
imageView.image = UIImage(named:"InfoIcon")
imageView.tag = tag
imageView.contentMode = .scaleAspectFit
view.addSubview(imageView)
}
}
You can play with y value for vertical alignment.

Related

Swift: Create multiple views through iteration with spacing without using StackView

I'm trying to create multiple labels with padding of 20 between each item without using stackView. My current logic has them all aligned on top of each other. Any thoughts whats wrong with the current code? Im not seeing it.
let sections = ["List", "Items", "News"]
var previousLabel: UILabel?
var sectionsArray = [UILabel]()
var leadConstraint: NSLayoutConstraint?
let navigationView = UIView(frame: CGRect(x: 20, y: 100, width: 100, height: 30))
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black
view.addSubview(navigationView)
navigationView.backgroundColor = .red
for label in sections {
let newLabel = UILabel(frame: CGRect.zero)
newLabel.text = label
newLabel.textColor = .white
newLabel.backgroundColor = .systemBlue
newLabel.sizeToFit()
navigationView.addSubview(newLabel)
if let previous = previousLabel {
leadConstraint = newLabel.leftAnchor.constraint(equalTo: previous.rightAnchor, constant: 20)
leadConstraint?.isActive = true
newLabel.topAnchor.constraint(equalTo: navigationView.topAnchor, constant: 0).isActive = true
newLabel.bottomAnchor.constraint(equalTo: navigationView.bottomAnchor, constant: 0).isActive = true
} else {
//1st item in section
newLabel.leftAnchor.constraint(equalTo: navigationView.leftAnchor, constant: 20).isActive = true
newLabel.topAnchor.constraint(equalTo: navigationView.topAnchor, constant: 0).isActive = true
newLabel.bottomAnchor.constraint(equalTo: navigationView.bottomAnchor, constant: 0).isActive = true
}
sectionsArray.append(newLabel)
previousLabel = newLabel
}
This is a image of the error message, i couldnt make out what exactly what constraint was having a problem.
Change
let newLabel = UILabel(frame: CGRect.zero)
To
let newLabel = UILabel(frame: CGRect.zero)
newLabel.translatesAutoresizingMaskIntoConstraints = false
This will expose other flaws in your implementation, but at least the labels will line up horizontally in the way you expect.

use #objc func to place a empty array of imageViews

I want to use my swift code to place a imageview everytime the func moveRight is called. I want each new imageView to be separated by a 50 on the y axis. Right now my code compiles but nothing is changing when the function is called. But in the debug area I am seeing the count increasing.
import UIKit
class ViewController: UIViewController {
var myArray = [UIImageView]()
var bt = UIButton()
var count : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(bt)
bt.backgroundColor = UIColor.systemOrange
bt.frame = CGRect(x: view.center.x - 0, y: view.center.y , width: 50, height: 50)
bt.addTarget(self, action: #selector(moveRight), for: .touchUpInside)
}
#objc func moveRight() {
print("Yes")
myArray.forEach({
$0.backgroundColor = UIColor.systemTeal
self.view.addSubview($0)
})
var ht = 50
myArray.insert(UIImageView(), at: count)
print("Your Count is ", count)
myArray[count].frame = CGRect(x: view.center.x - 0, y: view.center.y + CGFloat(ht), width: 50, height: 50)
count += 1
ht += 50
}
}
You need to move the ht variable outside the moveRight() method.
Currently every time you run the method you recreate the variable and set it's initial value to 50, and then use that to set your location.
You need to do
class ViewController: UIViewController {
var myArray = [UIImageView]()
var bt = UIButton()
var count : Int = 0
var ht = 50 //initialise it here, then update it in the moveRight() method
//etc
and then remove the equivalent line from the method.

Graphic Points are not working outside of viewDidLoad

I've created a Graphic Class to show for user a graphic points choose accordingly with user information.
If I use the graphicView object and add points at viewDidLoad, the graphic is presented correctly, if not there, the graphic presents no data.
See below the code for the Graphics and the code when I am requesting to mark the points.
class GraphView: UIView {
private struct Constants {
static let cornerRadiusSize = CGSize(width: 8.0, height: 8.0)
static let margin: CGFloat = 40.0
static let topBorder: CGFloat = 30
static let bottomBorder: CGFloat = 40
static let colorAlpha: CGFloat = 0.3
static let circleDiameter: CGFloat = 5.0
}
//1 - the properties for the gradient
var startColor: UIColor = UIColor.rgb(red: 14, green: 40, blue: 80)
var endColor: UIColor = UIColor.rgb(red: 14, green: 40, blue: 80)
//Weekly sample data
var graphPoints: [Int] = [0]
override func draw(_ rect: CGRect) {
let width = rect.width
let height = rect.height
let path = UIBezierPath(roundedRect: rect,
byRoundingCorners: UIRectCorner.allCorners,
cornerRadii: Constants.cornerRadiusSize)
path.addClip()
//2 - get the current context
let context = UIGraphicsGetCurrentContext()!
let colors = [startColor.cgColor, endColor.cgColor]
//3 - set up the color space
let colorSpace = CGColorSpaceCreateDeviceRGB()
//4 - set up the color stops
let colorLocations: [CGFloat] = [0.0, 1.0]
//5 - create the gradient
let gradient = CGGradient(colorsSpace: colorSpace,
colors: colors as CFArray,
locations: colorLocations)!
//6 - draw the gradient
var startPoint = CGPoint.zero
var endPoint = CGPoint(x: 0, y: self.bounds.height)
context.drawLinearGradient(gradient,
start: startPoint,
end: endPoint,
options: CGGradientDrawingOptions(rawValue: 0))
//calculate the x point
let margin = Constants.margin
let columnXPoint = { (column:Int) -> CGFloat in
//Calculate gap between points
let spacer = (width - margin * 2 - 4) / CGFloat((self.graphPoints.count - 1))
var x: CGFloat = CGFloat(column) * spacer
x += margin + 2
return x
}
// calculate the y point
let topBorder: CGFloat = Constants.topBorder
let bottomBorder: CGFloat = Constants.bottomBorder
let graphHeight = height - topBorder - bottomBorder
let maxValue = graphPoints.max()!
let columnYPoint = { (graphPoint:Int) -> CGFloat in
var y:CGFloat = CGFloat(graphPoint) / CGFloat(maxValue) * graphHeight
y = graphHeight + topBorder - y // Flip the graph
return y
}
// draw the line graph
UIColor.white.setFill()
UIColor.white.setStroke()
//set up the points line
let graphPath = UIBezierPath()
//go to start of line
graphPath.move(to: CGPoint(x:columnXPoint(0), y:columnYPoint(graphPoints[0])))
//add points for each item in the graphPoints array
//at the correct (x, y) for the point
for i in 1..<graphPoints.count {
let nextPoint = CGPoint(x:columnXPoint(i), y:columnYPoint(graphPoints[i]))
graphPath.addLine(to: nextPoint)
}
//Create the clipping path for the graph gradient
//1 - save the state of the context (commented out for now)
context.saveGState()
//2 - make a copy of the path
let clippingPath = graphPath.copy() as! UIBezierPath
//3 - add lines to the copied path to complete the clip area
clippingPath.addLine(to: CGPoint(x: columnXPoint(graphPoints.count - 1), y:height))
clippingPath.addLine(to: CGPoint(x:columnXPoint(0), y:height))
clippingPath.close()
//4 - add the clipping path to the context
clippingPath.addClip()
let highestYPoint = columnYPoint(maxValue)
startPoint = CGPoint(x:margin, y: highestYPoint)
endPoint = CGPoint(x:margin, y:self.bounds.height)
context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: CGGradientDrawingOptions(rawValue: 0))
context.restoreGState()
//draw the line on top of the clipped gradient
graphPath.lineWidth = 3.0
graphPath.stroke()
//Draw the circles on top of graph stroke
for i in 0..<graphPoints.count {
var point = CGPoint(x:columnXPoint(i), y:columnYPoint(graphPoints[i]))
point.x -= Constants.circleDiameter / 2
point.y -= Constants.circleDiameter / 2
let circle = UIBezierPath(ovalIn: CGRect(origin: point, size: CGSize(width: Constants.circleDiameter, height: Constants.circleDiameter)))
circle.fill()
}
//Draw horizontal graph lines on the top of everything
let linePath = UIBezierPath()
//top line
linePath.move(to: CGPoint(x:margin, y: topBorder))
linePath.addLine(to: CGPoint(x: width - margin, y:topBorder))
//center line
linePath.move(to: CGPoint(x:margin, y: graphHeight/2 + topBorder))
linePath.addLine(to: CGPoint(x:width - margin, y:graphHeight/2 + topBorder))
//bottom line
linePath.move(to: CGPoint(x:margin, y:height - bottomBorder))
linePath.addLine(to: CGPoint(x:width - margin, y:height - bottomBorder))
let color = UIColor(white: 1.0, alpha: Constants.colorAlpha)
color.setStroke()
linePath.lineWidth = 1.0
linePath.stroke()
}
}
I am trying to mark the points accordingly with the user input - see below:
func budgetAvailableCalculationFunction() {
let bankValue = (userSalary as NSString).integerValue
let bankPorcentage: Int = 100
let expenses = (userExpenses as NSString).integerValue
let calculation1: Int = expenses * bankPorcentage
let calculation2: Int = calculation1 / bankValue
let cashPorcentageAvailable = calculation2
let value: [Int] = [expenses]
self.setupGraphy(points: value)
progressView.progress = 0.0
progress.completedUnitCount = Int64(cashPorcentageAvailable)
progressView.setProgress(Float(self.progress.fractionCompleted), animated: true)
progressView.progressTintColor = UIColor.rgb(red: 239, green: 75, blue: 92)
progressView.backgroundColor = UIColor.rgb(red: 239, green: 75, blue: 92)
progressView.trackTintColor = .white
progressView.clipsToBounds = false
progressView.translatesAutoresizingMaskIntoConstraints = false
progressView.layer.cornerRadius = 0
porcentageLabelForMonth.text = "\(Int(self.progress.fractionCompleted * 100)) %"
}
The setupGraphy is just a function that returns an array of indexes that the user adds it.
Please note that using the same function at the viewDidLoad works:
self.setupGraphy(points: [100, 400, 200, 250])
enter image description here
Anyone?
Code for viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.rgb(red: 245, green: 246, blue: 250)
tableView.delegate = self
tableView.dataSource = self
tableView.register(LastTransactionsCell.self, forCellReuseIdentifier: "LastTransactionsCell")
tableView.backgroundColor = .clear
let now = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy - LLLL"
let nameOfMonth = dateFormatter.string(from: now)
currentMonthLabel.text = nameOfMonth
setupUIXViews()
fetchUserInfo()
//static data for now
*self.setupGraphy(points: [100, 400, 200, 250])*
DispatchQueue.main.asyncAfter(deadline: .now() + 2){
self.budgetAvailableCalculationFunction()
}
}
The way I managed to fix (seems like a temporary fix) is by creating a new Int variable at the top of the project and passing the fetch data from the DB to these variables. That way I can use them at viewDidLoad instead of inside the fetch function.

How to add multiple UIImageView programatically?

I'm struggling to get all the coupons (UIImage) to show up. For example, I have 3 coupons, when I run my code, it keeps on showing up the 3rd coupon's image only. How come it doesn't show the first 2? Thanks in advance.
override func viewDidLoad() {
var i: Int = 0
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
CouponsViewController.PopulateDataCoupons()
i=0
repeat {
let imageName = MyCoupons.siteCoupons[i]//"Coupons1.jpg"
let image = UIImage(named: imageName!)
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: 50, y: 50, width: 350, height: 300)
view.addSubview(imageView)
i += 1 }
while (i < MyCoupons.count-1)
}
You are putting each UIImageView object at the same location. Look at this line:
imageView.frame = CGRect(x: 50, y: 50, width: 350, height: 300)
Try changing the x or y coordinate on each iteration to see each one of the images, like this:
let height = 350
i = 0
repeat {
let imageName = MyCoupons.siteCoupons[i]//"Coupons1.jpg"
let image = UIImage(named: imageName!)
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: 50, y: i * height, width: 350, height: height)
view.addSubview(imageView)
i += 1
}
while (i < MyCoupons.count - 1)
Update:
As #Mick MacCallum points out. You should take the UIImageView's height into consideration. See the changes above.

Add UIImage to back View

Video
Hey :) Im kind of new to Stack and not sure if i should have edited this instead of adding a whole new question but i figured out a way to make it more clear. So in my code i want my textImages to only show up on the front of the UIimageViews, and the "back" image to show up on only the back side of my ImageView when flipped. Right now both Images are showing up on both parts. Is there a way to addSubview() to only the back of the ImagView :/ I do not want to use UItablevView if possible. Also in the video the last looped UIimageView works just like i want but not the ImageViews before it.
import UIKit
import Foundation
class ViewController: UIViewController {
#IBOutlet weak var mainScrollView: UIScrollView!
#IBOutlet var open: UIBarButtonItem!
var myImageArray = [UIImage]()
var back: UIImageView!
var front: UIImageView!
var showingFront = true
override func viewDidLoad() {
super.viewDidLoad()
let singleTap = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapped))
singleTap.numberOfTapsRequired = 1
mainScrollView.addGestureRecognizer(singleTap)
mainScrollView.isUserInteractionEnabled = true
mainScrollView.frame = view.frame
myImageArray = [textImg1, textImg2 ,textImg3, textImg4, textImg5]
for i in 0..<myImageArray.count {
front = UIImageView()
front.image = myImageArray[i]
front.contentMode = .scaleToFill
let yPosition = self.view.frame.height * CGFloat(i) + self.view.frame.height/2 - (self.view.frame.height / 1.1)/2
let xPosition = self.view.frame.width/2 - (self.view.frame.width / 1.1)/2
front.frame = CGRect(x: xPosition, y: yPosition, width: self.view.frame.width / 1.1, height: self.view.frame.height / 1.1)
front.layer.borderWidth = 5
mainScrollView.contentSize.height = mainScrollView.frame.height * CGFloat(i + 1)
mainScrollView.addSubview(front)
back = UIImageView(image: UIImage(named: "back.png"))
back.contentMode = .scaleToFill
back.frame = CGRect(x: xPosition, y: yPosition, width: self.view.frame.width / 1.1, height: self.view.frame.height / 1.1)
back.layer.borderWidth = 5
mainScrollView.addSubview(back)
}
}
func tapped() {
print("Hello")
if (showingFront) {
UIView.transition(from: front, to: back, duration: 1, options: UIViewAnimationOptions.transitionFlipFromRight, completion: nil)
showingFront = false
} else {
UIView.transition(from: back, to: front, duration: 1, options: UIViewAnimationOptions.transitionFlipFromLeft, completion: nil)
showingFront = true
}
}
}

Resources