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.
Related
I have a scroll view that has an image view embedded into it. The user clicks the "Take Photo" button, takes a picture, and then those photos are stored in an array and then displayed on the scrollable imageView. My issue is that after the didFinishSelectingMedia is called and the camera closes, the imageView always shows the first image in the array. I want the imageView to show the most recent image added to the array and scroll backward through the images until the user comes to the first image in the array. How do I make this happen?
Btw: Idk if this is even plausible, but I tried reversing the for loop and that didn't work.
I'm a new programmer, so please explain your answers.
Here's my code:
import UIKit
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet weak var scrollImageView: UIScrollView!
var imageTaken: [UIImage] = []
var imagePicker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
scrollImageView.frame = scrollImageView.frame
}
#IBAction func takePhotoButton(_ sender: Any) {
imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .camera
present(imagePicker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
imageTaken.append((info[.originalImage] as? UIImage)!)
imagePicker.dismiss(animated: true, completion: nil)
for i in 0..<imageTaken.count {
let imageView = UIImageView()
imageView.image = imageTaken[i]
imageView.contentMode = .scaleAspectFit
let xPosition = self.scrollImageView.frame.width * CGFloat(i)
imageView.frame = CGRect(x: xPosition, y: 0, width: self.scrollImageView.frame.width, height: self.scrollImageView.frame.height)
scrollImageView.contentSize.width = scrollImageView.frame.width * CGFloat(i + 1)
scrollImageView.addSubview(imageView)
}
}
}
Two options I guess you can take which ever one is seems easier.
OOP — Create an array of objects example:
struct TakePhotos {
var image : UIImage
var timestamp : Date
}
Then sort the array by the timestamp.
Use PHAsset
import Photos
Using PHAsset you should be able to retrieve the images taken from the user's album. Set a count when the user takes a new image.
Then you can run a query and sort the images in reverse order, they will already have the timestamp.
let photoOptions = PHFetchOptions()
photoOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
This is what I did in my application.
// FIXME: - Query only happens once this needs to be refactored so it happens when the album is updated on the device.
// Query only happens one time.
if imageArray.isEmpty {
let photoOptions = PHFetchOptions()
photoOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let allPhotos = PHAsset.fetchAssets(with: photoOptions)
let fetchOptions = PHImageRequestOptions()
fetchOptions.deliveryMode = .highQualityFormat
fetchOptions.resizeMode = .exact
fetchOptions.normalizedCropRect = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.width)
// Must be a synchronous call otherwise the view loads before images are retrieved (result is an empty view).
fetchOptions.isSynchronous = true
let imageSize : CGSize = CGSize(width: view.frame.width, height: view.frame.height)
//Retrieves Images using assets.
allPhotos.enumerateObjects { (assets, index, pointer) in
DispatchQueue.global(qos: .background).sync {
PHImageManager.default().requestImage(for: assets, targetSize: imageSize, contentMode: .aspectFit, options: fetchOptions) { (image, hashDictionary) in
guard let image = image else {return}
self.imageArray.append(image)
self.albumImages.append(ImageAlbum(images: image))
}
}
}
selectedImage.image = imageArray.first
selectedImage.contentMode = .scaleAspectFill
}
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.
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.
I have created some code which reads through an array and saves data for each index into variables which I then pass onto to a created label.
Below is the code:
example of data arr content :
["2,5","5,1"] two indexes inside array
for i in 0..<dataArr.count {
let element = dataArr[i]
let labelNum = UILabel()
label split = element.components(separatedBy: ",")
let num1 = split[0]
let num2 = split[1]
let num1Nnum2 = "number 1 :" + num1 + " number 2:" + num2
labelnum.text = num1Nnum2
labelnum.textAlignment = .center
labelnum.frame = CGRect( x:10, y:90, width:250, height: 80)
self.view.addSubview(labelnum)
}
how can I create it so that when the label is created the second time when it reads index[1] it creates a new label with same code but drop the label under the first label. I have tried to do :
labelnum[i] to attempt to create a new label using the value of index for example labelnum1 when i is = 1.
Any help will be Appreciated.
There is UIStackView in iOS which lets you add elements dynamically at the bottom or top of the existing views. You can always add a new label which automatically appears at the bottom of the view. You can also accomplish this with UITableView or UIScrollView.
Here is an example of UIStackView, dynamically appending new label below previous one. I hope you can infer this for your use case,
class ViewController: UIViewController {
var lastLabelCount = 0
var stackView: UIStackView!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
let tap = UITapGestureRecognizer(target: self,
action: #selector(tapped))
view.addGestureRecognizer(tap)
createViews()
}
func createViews() {
stackView = UIStackView(frame: .zero)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.alignment = .top
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.leftAnchor.constraint(equalTo: view.leftAnchor),
stackView.rightAnchor.constraint(equalTo: view.rightAnchor),
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
])
}
#objc func tapped() {
let label = UILabel(frame: .zero)
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.black
label.text = "Hi I am label \(lastLabelCount)"
stackView.addArrangedSubview(label)
lastLabelCount += 1
}
}
Create a variable to hold Y Position of The Label. and in each iteration add the height of previous label in Y Position variable to drop new label to below previous one.
class ViewController: UIViewController {
let dataArr = ["2,5","5,1"]
override func viewDidLoad() {
super.viewDidLoad()
var yPos = 90
for i in 0..<dataArr.count {
let element = dataArr[i]
let labelNum = UILabel()
let split = element.components(separatedBy: ",")
let num1 = split[0]
let num2 = split[1]
let num1Nnum2 = "number 1 :" + num1 + " number 2:" + num2
labelNum.text = num1Nnum2
labelNum.textAlignment = .center
labelNum.frame = CGRect( x:10, y:yPos, width:250, height: 80)
yPos += 80
self.view.addSubview(labelNum)
}
}
I have created views programmatically on sub views.
let view1 = UIView(frame: CGRectMake(30, cgfloat , 270, 120))
view1.backgroundColor = UIColor.whiteColor()
view1.layer.cornerRadius = 40.0
let Attdate: UILabel = UILabel()
Attdate.frame = CGRectMake(15, 10, 100, 25)
Attdate.backgroundColor = UIColor.orangeColor()
Attdate.textColor = UIColor.blackColor()
Attdate.font = UIFont(name: "BebasNeue", size: 106)
Attdate.textAlignment = NSTextAlignment.Left
Attdate.text = AttDate.description
view1.addSubview(Attdate)
I have an array as a response string from server which gives me an array of data. And I want to print that data into the labels. The labels should get called dynamically as the per the array length. And thats THE reason, I am trying to duplicate view1(my UIView object). I tried nskeyedarchiver(not sure how it will help).
extension UIView{
{
func copyView() -> AnyObject
{
return NSKeyedUnarchiver.unarchiveObjectWithData(NSKeyedArchiver.archivedDataWithRootObject(self))!
}
}
And declared:
let view1 = UIView()
let copiedView = view1.copyView() as! UIView
print("CopiedView:\(copiedView)")
But, no luck :(
Also, I tried many syntaxes to decrease the font size of a particular label, but none seemed to work.
Kindly reply.
To decrease the font size of a particular label, there are two options:
You can make the font size part of an initializer, so you can set it every time you have to create one, like this:
.
class CustomView: UIView {
init(fontSizeOfLabel : CGFloat) {
super.init(frame: CGRect(x: 30, y: 20, width: 270, height: 120))
// all your normal code in here
let Attdate: UILabel = UILabel()
Attdate.font = UIFont(name: "BebasNeue", size: fontSizeOfLabel)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Or you can loop over its subviews and change it after you have created it:
.
var instance = CustomView()
for subview in instance.subviews {
if var label = subview as? UILabel { //check if it can convert the subview into a UILabel, if it can it is your label
label.font = UIFont(name: "BebasNeue", size: 70)
}
}
I haven't tested the second solution, I have checked the first one