UIPopoverPresentationController programmatically created fills the screen - swift

The popup that I am creating programmatically fills the screen. As you can see from the example code, I have tried many ways to limit its size, but none are working. Also, the delegate methods are not being called. Any ideas? I have used CALayer successfully in the past for this kind of thing, but thought this would be simpler - maybe not.
#objc func touchDownHandler(sender: UISlider) {
let popoverController = UIViewController()
popoverController.view.backgroundColor = .red
popoverController.view.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
let textLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
textLabel.text = "Hello World"
textLabel.backgroundColor = .green
popoverController.view.addSubview(textLabel)
popoverController.modalPresentationStyle = UIModalPresentationStyle.popover
popoverController.preferredContentSize = CGSize(width: 200, height: 200)
if let popoverPresentation = popoverController.popoverPresentationController {
popoverPresentation.delegate = self
popoverPresentation.sourceRect = sender.frame
popoverPresentation.popoverLayoutMargins = UIEdgeInsets(top: 10, left: 10, bottom: 210, right: 210)
popoverPresentation.backgroundColor = .blue
self.controller.present(popoverController, animated: true, completion: {
print("pop over is visible")
})
}
}

Keep in mind that, as per Apple documentation, "In a horizontally compact environment, the .popover option behaves the same as UIModalPresentationStyle.fullScreen."
https://developer.apple.com/documentation/uikit/uimodalpresentationstyle/popover

Related

Unable to get an image to fit inside the bounds of a UITextField

I'm trying to get the image to scale down to the size of the UITextField and to position itself to the left side of it, but I am only able to get this:
What am I doing wrong?
Here is the relevant code:
//MARK: - Add Left Image to UITextField
func addLeftImageTo(txtField: UITextField, image img: UIImage) {
let leftImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
leftImageView.image = img
txtField.addSubview(leftImageView)
txtField.leftView = leftImageView
txtField.leftViewMode = .always
}
let image1 = UIImage(named: "stopwatch")
addLeftImageTo(txtField: timerTextField, image: image1!)
timerTextField.anchor(top: repsTextField.bottomAnchor, leading: view.safeAreaLayoutGuide.leadingAnchor, bottom: notesTextField.topAnchor, trailing: view.safeAreaLayoutGuide.trailingAnchor, padding: .init(top: 50.adjusted, left: 100.adjusted, bottom: 50.adjusted, right: 100.adjusted))
Try wrapping the UIImageView in a UIView
Your code:
func addLeftImageTo(txtField: UITextField, image img: UIImage) {
let leftImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
leftImageView.image = img
txtField.addSubview(leftImageView)
txtField.leftView = leftImageView
txtField.leftViewMode = .always
}
Example:
func addLeftImageTo(txtField: UITextField, image img: UIImage) {
let height = txtField.frame.size.height
let wrapView = UIView(frame: CGRect(x: 0, y: 0, width: height, height: height))
let leftImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: height, height: height))
leftImageView.image = img
leftImageView.contentMode = .scaleToFill
wrapView.addSubview(leftImageView)
txtField.addSubview(wrapView)
txtField.leftView = wrapView
txtField.leftViewMode = .always
}
Also, I added some lines including height, contentMode just in case.
I hope it will be helpful for you.
first make below method for adding image in UITextField
func setleftPaddingInTextfieldwithImage(_ textfield : UITextField, padding :
CGFloat, strImgname : String)
{
let imageView = UIImageView(image: UIImage(named: strImgname))
imageView.frame = CGRect(x: 0, y: 0, width: 15 , height: 15)
imageView.contentMode = .scaleAspectFit
let paddingView = UIView.init()
paddingView.frame = CGRect(x: padding, y: 0, width: padding, height: textfield.frame.size.height)
paddingView.addSubview(imageView)
textfield.leftView = paddingView
textfield.leftViewMode = .always
}
call it in your viewcontroller
self.setleftPaddingInTextfieldwithImage(YourTextField, padding: padding, strImgname: "stopwatch")

Edit- how can I get UIlabel to get in the middle of the container?

how can i get UIlabel to get in the middle of the container? and that text and view disappear equally and come to the next view.
EDIT: This code is working fine for me now
class TestViewController: UIViewController {
let container = UIView()
let redSquare = UIView()
let blueSquare = UIView()
let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
self.label.textColor = UIColor.orange
self.label.font = label.font.withSize(25)
self.label.numberOfLines = 0
self.label.center = self.blueSquare.center
self.label.textAlignment = NSTextAlignment.center
self.label.text =
"sosdihfiosdfhsdhfdisfhsdfhdsoifhsdofhsdifhdsofhdsofhsdohdsfdosdohdfh"
self.blueSquare.addSubview(label)
self.label.frame = CGRect(x: 45, y: 120, width: 300, height: 100)
self.container.frame = CGRect(x: 7, y: 200, width: 400, height: 500)
self.view.addSubview(container)
self.redSquare.frame = CGRect(x: 0, y: 0, width: 400, height: 500)
self.blueSquare.frame = redSquare.frame
self.redSquare.backgroundColor = UIColor.darkGray
self.blueSquare.backgroundColor = UIColor.darkGray
self.container.addSubview(self.redSquare)
}
Define two labels like:
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 30))
let label2 = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 30))
then inside viewDidLoad() before adding redSquare to container fix some labels's property and add label's to blue and red square:
label.center = self.redSquare.center
label.textAlignment = NSTextAlignment.center
label.text = "same text"
label2.textAlignment = NSTextAlignment.center
label2.center = self.blueSquare.center
label2.text = "same text"
self.blueSquare.addSubview(label2)
self.redSquare.addSubview(label)

retrieve information from firebase and set as an image and label in UICollectionView

I am trying to retrieve some data that Is stored in firebase, and allow that Information to be displayed inside of a collection view. Such as profileImageUrl, name label and email label.
I have tried to read the documentation on firebase and apple website, but for some reason, my code returns plain. I don't receive any errors, but my code is not running as expected. When I go to my app's profile view, the only texts that are displayed are the placeholder that I programmatically placed.
let profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "users")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
return imageView
}()
let nameLabel: UILabel = {
let label = UILabel()
label.text = "User's Name"
label.font = UIFont.boldSystemFont(ofSize: 18)
label.textColor = GREEN_Theme
return label
}()
let uidLabel: UILabel = {
let label = UILabel()
label.text = "User's uid"
label.font = UIFont.boldSystemFont(ofSize: 16)
label.textColor = GREEN_Theme
return label
}()
let emailLabel: UILabel = {
let label = UILabel()
label.text = "User's email"
label.font = UIFont.boldSystemFont(ofSize: 16)
label.textColor = GREEN_Theme
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
func setupView() {
if Auth.auth().currentUser != nil {
guard let uid = Auth.auth().currentUser?.uid else {
return }
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String : Any] else {
return }
let user = CurrentUser(uid: uid, dictionary: dictionary)
self.uidLabel.text = uid
self.nameLabel.text = user.name
self.emailLabel.text = user.email
self.profileImageView.loadImageUsingCacheWithUrlString(user.profileImageUrl)
}, withCancel: { (err) in
print("attempting to load information")
})
self.addSubview(profileImageView)
self.addSubview(nameLabel)
self.addSubview(emailLabel)
self.addSubview(uidLabel)
profileImageView.anchors(top: topAnchor, topPad: 125, bottom: bottomAnchor, bottomPad: 75, left: leftAnchor, leftPad: 20, right: rightAnchor, rightPad: 250, height: 20, width: 20)
nameLabel.anchors(top: profileImageView.bottomAnchor, topPad: -50, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 0, height: 20, width: 20)
emailLabel.anchors(top: nameLabel.bottomAnchor, topPad: -80, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 125, height: 20, width: 20)
uidLabel.anchors(top: emailLabel.bottomAnchor, topPad: -40, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 0, height: 20, width: 20)
profileImageView.layer.zPosition = 10
nameLabel.layer.zPosition = 10
emailLabel.layer.zPosition = 10
profileImageView.layer.borderWidth = 2.0
profileImageView.layer.cornerRadius = 50
profileImageView.layer.borderWidth = 2.0
profileImageView.layer.borderColor = UIColor.white.cgColor
profileImageView.layer.masksToBounds = true
}
}
I would really appreciate it if someone could help critique what I have, as well as help show me what the correct implementation would look like. Any and all help would be greatly appreciated
There are several things that are not correct.
your cell should not be the one in charge of downloading or doing any network call, leave that to the viewController or even better a separate handler for all the network calls.
Firebase is asynchronous so by the time your data is downloaded all the views have already been set and displayed, that is why you are only seeing your placeholders.
You have a lot of redundant code for checking optionals, this is not much of a problem but it is not needed.
You should really think about changing all this code and separating the different tasks to different handlers. For example, you could create a separate class that controls all the calls to Firebase and returns the values with completion blocks, from the viewController or the collectionView itself ask the network handler to give you the data that you need, pass this data to the cell and let the cell display the data, the cell should only display the data, not download it or ask for networks requests. I cannot write up a complete example for you but I think that if you go around in google you will find a lot of very good examples and tutorials.
Anyway, just to get you started and so you have a look at the mistakes that you have in your code, here is a quick fix for it. Even if it works, don't take for granted that everything is fixed. You should really refactor all the code and separate the different tasks. Hope this helps at least so you can solve the problem of displaying your downloaded data in the cell.
let profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "users")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
return imageView
}()
let nameLabel: UILabel = {
let label = UILabel()
label.text = "User's Name"
label.font = UIFont.boldSystemFont(ofSize: 18)
label.textColor = GREEN_Theme
return label
}()
let uidLabel: UILabel = {
let label = UILabel()
label.text = "User's uid"
label.font = UIFont.boldSystemFont(ofSize: 16)
label.textColor = GREEN_Theme
return label
}()
let emailLabel: UILabel = {
let label = UILabel()
label.text = "User's email"
label.font = UIFont.boldSystemFont(ofSize: 16)
label.textColor = GREEN_Theme
return label
}()
// Here you add a variable that is observed for SET, once a set happens bind() will be called and the views will be updated.
var currentUser : CurrentUser? {
didSet {
self.bind()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
//...
}
// setupView() should only add the UI appearance, and in this case it is calling firebase and setting currentUser, but it should not do that. This is what you have to refactor.
func setupView() {
self.addSubview(profileImageView)
self.addSubview(nameLabel)
self.addSubview(emailLabel)
self.addSubview(uidLabel)
profileImageView.anchors(top: topAnchor, topPad: 125, bottom: bottomAnchor, bottomPad: 75, left: leftAnchor, leftPad: 20, right: rightAnchor, rightPad: 250, height: 20, width: 20)
nameLabel.anchors(top: profileImageView.bottomAnchor, topPad: -50, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 0, height: 20, width: 20)
emailLabel.anchors(top: nameLabel.bottomAnchor, topPad: -80, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 125, height: 20, width: 20)
uidLabel.anchors(top: emailLabel.bottomAnchor, topPad: -40, bottom: bottomAnchor, bottomPad: 0, left: profileImageView.leftAnchor, leftPad: 0, right: rightAnchor, rightPad: 0, height: 20, width: 20)
profileImageView.layer.zPosition = 10
nameLabel.layer.zPosition = 10
emailLabel.layer.zPosition = 10
profileImageView.layer.borderWidth = 2.0
profileImageView.layer.cornerRadius = 50
profileImageView.layer.borderWidth = 2.0
profileImageView.layer.borderColor = UIColor.white.cgColor
profileImageView.layer.masksToBounds = true
guard let user = Auth.auth().currentUser else { return }
let uid = user.uid
let reference = Database.database().reference().child("users").child(uid)
reference.observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String : Any] else { return }
self.currentUser = CurrentUser(uid: uid, dictionary: dictionary)
}, withCancel: { (err) in
print("attempting to load information")
})
}
// This is the method that updates the cell views.
func bind() {
self.uidLabel.text = currentUser.uid
self.nameLabel.text = currentUser.name
self.emailLabel.text = currentUser.email
self.profileImageView.loadImageUsingCacheWithUrlString(currentUser.profileImageUrl)
}

How to give UIImageView attributes using if/else function?

How can I trim this function down to be as lean as possible? The only difference between the if and else blocks is the height of the UiImageView--if true, height is 300, else 150. I can't seem to figure out the best way to refactor this function without either redundant code (like in the example) or with a really long function that just seems way too long for something that seems rather simple.
I left the example crude to make the intention clear.
class ProfileViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
banner(profileHasCustomBanner: false)
}
// banner
func banner(profileHasCustomBanner: Bool) {
if profileHasCustomBanner == true {
let bannerImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 300))
bannerImageView.image = #imageLiteral(resourceName: "the-banner")
view.contentMode = .scaleAspectFit
view.addSubview(bannerImageView)
} else {
let bannerImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 150))
bannerImageView.image = #imageLiteral(resourceName: "the-banner")
view.contentMode = .scaleAspectFit
view.addSubview(bannerImageView)
}
}
}
Define a height variable based on the condition.
func banner(profileHasCustomBanner: Bool) {
let height: CGFloat = profileHasCustomerBanner ? 300 : 150
let bannerImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: height))
bannerImageView.image = #imageLiteral(resourceName: "the-banner")
view.contentMode = .scaleAspectFit
view.addSubview(bannerImageView)
}

Swift: Setting progress of NSProgressIndicator

I have tried to setup my own NSProgressIndicator with the following method.
class ProgressViewController : NSViewController
{
override func loadView()
{
let view = NSView(frame: NSMakeRect(0,0,300,120))
view.wantsLayer = true
view.layer?.borderWidth = 0
self.view = view
let text = NSTextField(frame: NSRect(x: 20, y: 45, width: 260, height: 20))
text.drawsBackground = true
text.isBordered = false
text.backgroundColor = NSColor.controlColor
text.isEditable = false
self.view.addSubview(text)
text.stringValue = "Progress Text"
let indicator = NSProgressIndicator(frame: NSRect(x: 20, y: 20, width: 260, height: 20))
indicator.minValue = 0.0
indicator.maxValue = 100.0
indicator.doubleValue = 33.0
self.view.addSubview(indicator)
}
}
My problem is that the indicator is always shown full. Am I missing a trick?
The default style is indeterminate. Set it to false and everything should be OK:
indicator.isIndeterminate = false