Set UIBarButtonItem with circular Image profile - ios

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)
}

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:

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

MKMarkerAnnotationView has a truncated image

I want to get a UIImage from a MKMarkerAnnotationView but with the following code the image is truncated (at the top and bottom, see the screenshot)
let newAnnotation = SimpleAnnotation(sourceCoordinate: annotation.coordinate, sourceTitle: "Hello", sourceSubtitle: "world")
let pinAnnotation = MKMarkerAnnotationView(annotation: newAnnotation, reuseIdentifier: "Test")
pinAnnotation.markerTintColor = UIColor.red
pinAnnotation.glyphText = "1"
pinAnnotation.animatesWhenAdded = false
pinAnnotation.glyphTintColor = UIColor.white
pinAnnotation.titleVisibility = .hidden
pinAnnotation.subtitleVisibility = .hidden
UIGraphicsBeginImageContextWithOptions(pinAnnotation.bounds.size, false, 0.0)
pinAnnotation.drawHierarchy(in: CGRect(x:0,
y:0,
width:pinAnnotation.bounds.width,
height:pinAnnotation.bounds.height),
afterScreenUpdates: true)
let snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Any idea why it's truncated? In the Xcode debugger I can see the image is already truncated in the pinAnnotation (before I use UIGraphics... to get the UIImage)
I have found the solution! I have just to set the contentMode to scaleAspectFit and to change the bounds to the size of the image I need (40x40px).
Here's the code displaying the full MKMarkerAnnotationView.
let pinAnnotation = MKMarkerAnnotationView()
pinAnnotation.markerTintColor = UIColor.red
pinAnnotation.glyphText = "1"
pinAnnotation.animatesWhenAdded = false
pinAnnotation.glyphTintColor = UIColor.white
pinAnnotation.titleVisibility = .hidden
pinAnnotation.subtitleVisibility = .hidden
pinAnnotation.contentMode = .scaleAspectFit
pinAnnotation.bounds = CGRect(x: 0, y: 0, width: 40, height: 40)
UIGraphicsBeginImageContextWithOptions(pinAnnotation.bounds.size, false, 0.0)
pinAnnotation.drawHierarchy(in: CGRect(x:0,
y:0,
width:pinAnnotation.bounds.width,
height:pinAnnotation.bounds.height),
afterScreenUpdates: true)
let snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
This page Demystifying iOS Layout has helped me a lot to find this solution.
I can't get the drawHierarchy to work I just get back an empty image.

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

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

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))

Resources