Navigation bar custom image view issue in iOS 11 - ios

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

Related

how to add image view with some constraints programmatically using swift?

I am new in iOS Development. If using storyboard, I can place an Image view in view controller like this
I need to make something like that programmatically.
so I have custom view from a library called RevealingSplashView , but I need to add an image view to that custom UI View. I just know to add the image view, maybe something like this
let imageName = "yourImage.png"
let image = UIImage(named: imageName)
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: 0, y: 0, width: 100, height: 200)
revealingSplashView.addSubview(imageView)
but I don't know how to set that constraint to image view to
a. align to center x to superview
b. proportional width to superview 0.8
c. height constraint = 25
d. align bottom to safe area = 32
how to do that ?
here is the code I use before want to add image view
let screenSize = UIScreen.main.bounds
let iconWidth = (screenSize.width) * 0.8
let iconHeight = iconWidth * 1 // ratio 1:1
revealingSplashView = RevealingSplashView(iconImage: UIImage(named: "Loading Page Asset")!,iconInitialSize: CGSize(width: iconWidth, height: iconHeight), backgroundColor: AppColor.mainYellow.getUIColor())
revealingSplashView.animationType = SplashAnimationType.twitter
revealingSplashView.imageView?.contentMode = .scaleAspectFill
// add loading indicator to RevealingSplashView Programatically
revealingSplashViewIndicator.color = UIColor.white
revealingSplashViewIndicator.frame = CGRect(x: 0.0, y: 0.0, width: 30.0, height: 30.0)
revealingSplashViewIndicator.center = CGPoint(x: self.view.center.x, y: self.view.center.y + (iconHeight/2) + 64 )
revealingSplashView.addSubview(revealingSplashViewIndicator)
revealingSplashViewIndicator.bringSubviewToFront(self.revealingSplashView)
revealingSplashViewIndicator.startAnimating()
let window = UIApplication.shared.keyWindow
window?.addSubview(revealingSplashView)
I recommend using anchors:
let imageName = "yourImage.png"
let image = UIImage(named: imageName)
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: 0, y: 0, width: 100, height: 200)
revealingSplashView.addSubview(imageView)
// you need to turn off autoresizing masks (storyboards do this automatically)
imageView.translatesAutoresizingMaskIntoConstraints = false
// setup constraints, it is recommended to activate them through `NSLayoutConstraint.activate`
// instead of `constraint.isActive = true` because of performance reasons
NSLayoutConstraint.activate([
imageView.centerXAnchor.constraint(equalTo: revealingSplashView.centerXAnchor),
imageView.widthAnchor.constraint(equalTo: revealingSplashView.widthAnchor, multiplier: 0.8),
imageView.heightAnchor.constraint(equalToConstant: 25),
imageView.bottomAnchor.constraint(equalTo: revealingSplashView.safeAreaLayoutGuide.bottomAnchor, constant: -32),
])
To add constraints programmatically you need to set
imageView.translatesAutoresizingMaskIntoConstraints = false
then you can set the constraints:
imageView.widthAnchor.constraint(equalTo: revealingSplashView.widthAnchor, multiplier: 0.8).isActive = true
imageView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 25).isActive = true
imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
if you want to activate constraints you need to set
isActive = true
also if you support iOS < 11 you need to put a control because you don't have the safeArea
revealingSplashView.addSubview(imageView)
imageView.centerXAnchor.constraint(equalTo: revealingSplashView.centerXAnchor).isActive = true
imageView.widthAnchor.constraint(equalTo: revealingSplashView.widthAnchor, multiplier: 0.8).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 25).isActive = true
imageView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
imageView.translatesAutoresizingMaskIntoConstraints = false

Swift UITapGesture on view in a titleView not working

I have a UINavigationItem and I set it's titleView to a UIView which has a UILabel and UIImageView embedded. I'm attempting to add a UITapGestureRecognizer to the view but it doesn't seem to work. Any solutions? Also, adding a gestureRecognizer to the whole navigationBar isn't an option as I have a rightBarButtonItem and want to make use of the back button.
Here is my code:
func configureTitleView() {
guard let profile = profile else {
// Pop navController
return
}
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.clipsToBounds = true
let imageURL = URL(string: profile!.firstProfilePicture!)
profileImageView.sd_setImage(with: imageURL)
containerView.addSubview(profileImageView)
profileImageView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true
profileImageView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 36).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 36).isActive = true
profileImageView.layer.cornerRadius = 36 / 2
let nameLabel = UILabel()
containerView.addSubview(nameLabel)
nameLabel.text = profile!.displayName!
nameLabel.textColor = .white
nameLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.leftAnchor.constraint(equalTo: profileImageView.rightAnchor, constant: 8).isActive = true
nameLabel.centerYAnchor.constraint(equalTo: profileImageView.centerYAnchor).isActive = true
nameLabel.rightAnchor.constraint(equalTo: containerView.rightAnchor).isActive = true
nameLabel.heightAnchor.constraint(equalTo: profileImageView.heightAnchor).isActive = true
containerView.centerXAnchor.constraint(equalTo: titleView.centerXAnchor).isActive = true
containerView.centerYAnchor.constraint(equalTo: titleView.centerYAnchor).isActive = true
self.navigationItem.titleView = titleView
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.openProfile))
tapGesture.numberOfTapsRequired = 1
titleView.addGestureRecognizer(tapGesture)
titleView.isUserInteractionEnabled = true
}
Beginning with iOS 11, views added to toolbars as UIBarButtonItem using UIBarButtonItem(customView:) are now laid out using auto layout. This includes title views added to a UINavigationBar through the navigationItem.titleView property of a UIViewController. You should add sizing constraints on your titleView. For example:
titleView.widthAnchor.constraint(equalToConstant: 100).isActive = true
titleView.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
Otherwise, auto layout will use the intrinsic content size of your title view which is CGSize.zero. Gestures are masked to the bounds of the view they are attached to even if the sub views of that view are not. Because the bounds of titleView without constraints is CGRect.zero it will never fire. Add constraints and it works as expected.
For more information see the WWDC 2017 session Updating your app for iOS 11.
You do not need to add explicit height and width constant constraint to custom view.
Just add subviews to custom view, add width and height anchor.
let customView = UIView()
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
customView.addSubview(button)
[
button.widthAnchor.constraint(equalTo: customView.widthAnchor),
button.heightAnchor.constraint(equalTo: customView.heightAnchor),
].forEach({$0.isActive = true})
navigationItem.titleView = customView

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

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