Swift3 iOS -Rounded ImageView in Navigation TitleView Keeps Showing Square? - ios

I have a rounded imageView that I use for the profile pic in my app. I use the code several times throughout the app in tableView cells and on different views in view controllers. The profile pic always displays as round. When I use the below code to set the profile pic to round inside the navItem's titleView it only displays as a square.
Why doesn't it display round in the navItem's titleView?
var url: String?//it's already set with some value
override func viewDidLoad() {
super.viewDidLoad(){
setTitleView(urlStr: url)
{
func setTitleView(urlStr: String?){
let titleView = UIView()
titleView.frame = CGRect(x: 0, y: 0, width: 44, height: 44)
let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
titleView.addSubview(containerView)
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
imageView.layer.cornerRadius = imageView.frame.size.width / 2
imageView.clipsToBounds = true
imageView.layer.borderColor = UIColor.white.cgColor
imageView.layer.borderWidth = 0.5
imageView.backgroundColor = UIColor.white
if let urlStr = urlStr{
let url = URL(string: urlStr)
imageView.sd_setImage(with: url!)
}
containerView.addSubview(imageView)
imageView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true
imageView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 40).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 40).isActive = true
containerView.centerXAnchor.constraint(equalTo: titleView.centerXAnchor).isActive = true
containerView.centerYAnchor.constraint(equalTo: titleView.centerYAnchor).isActive = true
navigationItem.titleView = titleView
}

Looks like imageView.layer.cornerRadius = imageView.frame.size.width / 2 is set to 0. imageView.frame.size.width is equal to 0 here.
Instead of let imageView = UIImageView(), you can predefine the frame when creating the imageView with let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))

Related

How to put a UIStackview inside a UIView in Swift programatically

I want to put a Stackview of 5 images inside a UIView. Basically what I want is to make a rounded button with a shadow and inside the button 5 different small images horizontally.
What I already have is a viewcontroller with each a declaration and setup function. I am able to make the UIView.
Here is an image of what I am trying to achieve:
How do I solve this?
Some code I already have:
private let btnUIView: UIView = {
let btnUIView = UIView(frame: CGRect(x: 50, y: 50, width: 343, height: 77))
btnUIView.layer.shadowColor = UIColor.black.cgColor
btnUIView.layer.shadowOpacity = 0.5
btnUIView.layer.shadowOffset = .zero
btnUIView.layer.shadowRadius = 3
btnUIView.backgroundColor = .white
btnUIView.translatesAutoresizingMaskIntoConstraints = false
btnUIView.layer.borderWidth = 0
btnUIView.layer.borderColor = UIColor.gray.cgColor
return btnUIView
}()
private let btnStackView: UIStackView = {
let image = UIStackView()
image.translatesAutoresizingMaskIntoConstraints = false
image.distribution = .fillEqually
// image.spacing = 60
return image
}()
func setupBtnView(){
view.addSubview(btnUIView)
btnUIView.addSubview(btnStackView)
NSLayoutConstraint.activate([
btnUIView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
btnUIView.topAnchor.constraint(equalTo: btnText.bottomAnchor, constant: 15),
btnUIView.heightAnchor.constraint(equalToConstant: btnUIView.frame.height),
btnUIView.widthAnchor.constraint(equalToConstant: btnUIView.frame.width),
])
let imgone = UIImageView(image: #imageLiteral(resourceName: "imgtest1"))
let imgtwo = UIImageView(image: #imageLiteral(resourceName: "imgtest1"))
let imgthree = UIImageView(image: #imageLiteral(resourceName: "imgtest1"))
let imgfour = UIImageView(image: #imageLiteral(resourceName: "imgtst1"))
let imgfive = UIImageView(image: #imageLiteral(resourceName: "imgtest1"))
btnStackView.addArrangedSubview(imgone)
btnStackView.addArrangedSubview(imgtwo)
btnStackView.addArrangedSubview(imgthree)
btnStackView.addArrangedSubview(imgfour)
btnStackView.addArrangedSubview(imgfive)
imgone.contentMode = .scaleAspectFill
imgtwo.contentMode = .scaleAspectFill
imgthree.contentMode = .scaleAspectFill
imgfour.contentMode = .scaleAspectFill
imgfive.contentMode = .scaleAspectFill
imgone.clipsToBounds = true
imgtwo.clipsToBounds = true
imgthree.clipsToBounds = true
imgfour.clipsToBounds = true
imgfive.clipsToBounds = true
}
func setupImages(){
view.addSubview(btnStackView)
NSLayoutConstraint.activate([
btnStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
btnStackView.topAnchor.constraint(equalTo: btnUIView.bottomAnchor, constant: 20),
btnStackView.widthAnchor.constraint(equalToConstant: btnStackView.frame.width)
])
What I see in the app, an empty UIView:
This seems pretty straightforward. It took me about two minutes to write the code that gets us this:
That's just a sketchy rendering of what you're after, but it shows that the basic idea is simple enough. All I did was:
Create the outer view, configure it with a rounded border, and add it as a subview.
Create the stack view, configure it with equal spacing etc., and add it as a subview to the outer view.
Create five image views with images (just circles here) and add them as arranged subviews to the stack view.
Literally just 11 lines of code inside my viewDidLoad.
As for your code (which you have now shown), the chief problem is likely that the constraints make no sense. Here's my correction of your code (substituting my own circle image, for test purposes); this is the entire code of my test app view controller:
private let btnUIView: UIView = {
let btnUIView = UIView()
btnUIView.layer.shadowColor = UIColor.black.cgColor
btnUIView.layer.shadowOpacity = 0.5
btnUIView.layer.shadowOffset = .zero
btnUIView.layer.shadowRadius = 3
btnUIView.backgroundColor = .white
btnUIView.translatesAutoresizingMaskIntoConstraints = false
btnUIView.layer.borderWidth = 0
btnUIView.layer.borderColor = UIColor.gray.cgColor
return btnUIView
}()
private let btnStackView: UIStackView = {
let image = UIStackView()
image.translatesAutoresizingMaskIntoConstraints = false
image.distribution = .fillEqually
image.alignment = .center
return image
}()
override func viewDidLoad() {
super.viewDidLoad()
let r = UIGraphicsImageRenderer(size: CGSize(width: 30, height: 30))
let im = r.image { _ in UIBezierPath.init(ovalIn: CGRect(x: 1, y: 1, width: 28, height: 28)).stroke() }
let imgone = UIImageView(image: im)
let imgtwo = UIImageView(image: im)
let imgthree = UIImageView(image: im)
let imgfour = UIImageView(image: im)
let imgfive = UIImageView(image: im)
btnStackView.addArrangedSubview(imgone)
btnStackView.addArrangedSubview(imgtwo)
btnStackView.addArrangedSubview(imgthree)
btnStackView.addArrangedSubview(imgfour)
btnStackView.addArrangedSubview(imgfive)
setupBtnView()
}
func setupBtnView(){
view.addSubview(btnUIView)
btnUIView.addSubview(btnStackView)
NSLayoutConstraint.activate([
btnUIView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
btnUIView.topAnchor.constraint(equalTo: view.topAnchor, constant: 40),
btnUIView.heightAnchor.constraint(equalToConstant: 100),
btnUIView.widthAnchor.constraint(equalToConstant: 300),
])
NSLayoutConstraint.activate([
btnStackView.leadingAnchor.constraint(equalTo: btnUIView.leadingAnchor),
btnStackView.trailingAnchor.constraint(equalTo: btnUIView.trailingAnchor),
btnStackView.topAnchor.constraint(equalTo: btnUIView.topAnchor),
btnStackView.bottomAnchor.constraint(equalTo: btnUIView.bottomAnchor),
])
}
Result:

Set UIBarButtonItem with circular Image profile

How to set UIBarButton with circular image profile
func loadProfile(){
let url = URL(string: "https://res.cloudinary.com/demo/image/upload/v1312461204/sample.jpg")!
let data = try! Data(contentsOf: url)
let img = UIImage(data: data)
let imageView = UIImageView(frame: CGRect(x: 0.0, y: 0.0, width: 40.0, height: 40.0))
imageView.image = img?.withRenderingMode(.alwaysOriginal)
imageView.layer.cornerRadius = 20.0
imageView.layer.masksToBounds = true
let barButton = UIBarButtonItem(customView: imageView)
self.tabBarController?.navigationItem.setRightBarButton(barButton, animated: false)
Failed to set Constrains thats why its behaviors!
imageView.widthAnchor.constraint(equalToConstant: buttonWidth).isActive = true
imageView.heightAnchor.constraint(equalToConstant: buttonHeight).isActive = true
Fixed Issue.
Have you tried set content mode in your imageView ?
imageView.contentMode = .scaleAspectFill
For anyone that finds this, as suggested by kiran, setting the imageView frame size is not enough. You should also set the height and width constraints. Otherwise, any animation to the navigation bar will result in the imageView.image occupying the whole navigationBar width, as seen in the image posted by the OP.
func loadProfile(){
let url = URL(string: "https://res.cloudinary.com/demo/image/upload/v1312461204/sample.jpg")!
let data = try! Data(contentsOf: url)
let img = UIImage(data: data)
let imageView = UIImageView(frame: CGRect(x: 0.0, y: 0.0, width: 40.0, height: 40.0))
// NEW CODE HERE: Setting the constraints
imageView.widthAnchor.constraint(equalToConstant: 40).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 40).isActive = true
imageView.image = img?.withRenderingMode(.alwaysOriginal)
imageView.layer.cornerRadius = 20.0
imageView.layer.masksToBounds = true
let barButton = UIBarButtonItem(customView: imageView)
self.tabBarController?.navigationItem.setRightBarButton(barButton, animated: false)
}

Navigation bar custom image view issue in iOS 11

I'm setting a normal image view as a custom view for my nav bar item but this is what it's happening:
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 34, height: 34))
imageView.kf.setImage(with: user.profilePictureURL)
if user.profilePictureURL == nil {
imageView.image = #imageLiteral(resourceName: "ProfilePlaceholderSuit")
}
imageView.backgroundColor = .white
imageView.layer.masksToBounds = true
imageView.layer.cornerRadius = 17
imageView.layer.borderWidth = 1
imageView.layer.borderColor = UIColor.white.cgColor
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: imageView)
print("ok, so the image view frame is", imageView.frame) // 0, 0, 34, 34
This types of issue faced by many developers because of in iOS 11 UIBarButtonItem uses auto-layout instead of frames.
So you just want to set profile image on top right barButton items then please check my answer..
Other wise you can add constraints of imageView in you code like below
let widthConstraint = imageView.widthAnchor.constraint(equalToConstant: 34)
let heightConstraint = imageView.heightAnchor.constraint(equalToConstant: 34)
heightConstraint.isActive = true
widthConstraint.isActive = true
This was a problem with the Kingfisher framework. I've switched to SDWebImage and it works fine (sometimes).
Edit:
This worked
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.heightAnchor.constraint(equalToConstant: 34).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 34).isActive = true

UINavigationBar contentMode not being set

I am trying to make the background of the UINavigationBar an image, and the image isn't the exact size of the bar. I can't seem to set the contentMode of the bar correctly for some reason. This is the code I have:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// ...
let image = UIImage(named: object?.imageName ?? "")
navigationController?.navigationBar.alpha = 0
navigationController?.navigationBar.setBackgroundImage(image, for: .default)
self.navigationController?.navigationBar.contentMode = .scaleAspectFill
UIView.animate(withDuration: Double(UINavigationControllerHideShowBarDuration), animations: {
self.navigationController?.navigationBar.alpha = 1
})
}
I don't know what I'm doing wrong, or maybe contentMode just doesn't do anything for UINavigationBar.
What's currently happening is the background image is just presented again like mosaic
Thanks
You can create UIView and add Image and Text to it, but you must set constraints
let titleView = UIView()
titleView.frame = CGRect(x: 0, y: 0, width: 100, height: 40)
let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
titleView.addSubview(containerView)
let profileImageView = UIImageView()
profileImageView.translatesAutoresizingMaskIntoConstraints = false
profileImageView.contentMode = .scaleAspectFill
profileImageView.layer.cornerRadius = 20
profileImageView.clipsToBounds = true
containerView.addSubview(profileImageView)
profileImageView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true
profileImageView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 40).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 40).isActive = true
self.navigationItem.titleView = titleView
titleView.addGestureRecognizer(UITapGestureRecognizer(target:
self, action: #selector(func)))
I hope that I helped you

Why doesn't a tap gesture recognizer work when creating a variable?

I am new to Swift, and I have been working on a project and ran into a peculiar issue that I fixed, but wasn't quite sure why the solution worked.
I have a class UserViewController where I do something like:
class UserViewController: UIViewController {
let profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "profilepic")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
imageView.frame = CGRect.init(x: 0, y: 0, width: 30, height: 30) //CGRectMake(0, 0, 30, 30)
imageView.layer.cornerRadius = 0.5 * imageView.bounds.size.width
imageView.clipsToBounds = true
return imageView
}()
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
func setupView() {
view.addSubview(profileImageView)
profileImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
profileImageView.topAnchor.constraint(equalTo:topLayoutGuide.bottomAnchor, constant: 24).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 80).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 80).isActive = true
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleSelectProfilePicture))
profileImageView.addGestureRecognizer(tapGestureRecognizer)
profileImageView.isUserInteractionEnabled = true
}
I was trying to add a tap gesture recognizer to my UIImageView. What I was doing before was actually adding the recognizer in my initialization for profileImageView like this:
let profileImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "profilepic")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
imageView.frame = CGRect.init(x: 0, y: 0, width: 30, height: 30) //CGRectMake(0, 0, 30, 30)
imageView.layer.cornerRadius = 0.5 * imageView.bounds.size.width
imageView.clipsToBounds = true
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleSelectProfilePicture))
profileImageView.addGestureRecognizer(tapGestureRecognizer)
profileImageView.isUserInteractionEnabled = true
return imageView
}()
When I was doing this, the recognizer was not working (I would tap on the image and nothing would happen, the handler function was never invoked). When I pulled out the three lines that created/added the gesture recognizer to my image view, and moved them into setupView() the tap was recognized, and everything worked as expected.
For the betterment of my understanding, why is this the case? Why can't I add the gesture recognize in the initialization of my image view and have it work?
Thanks in advance.
You were modifying the not-yet returned variable in these two lines in your old code:
profileImageView.addGestureRecognizer(tapGestureRecognizer)
profileImageView.isUserInteractionEnabled = true
Replace profileImageView with imageView and it will work.

Resources