CollectionView inside of Container View not performing tapGesture - ios

So the setup I have is a Container View with a Collection View inside of that it. Inside of the Collection view is where I have "messageImageView ". However, when I try and tap messageImageView, it doesn't perform the function. Below is the code i'm using
let messageImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.cornerRadius = 24
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleZoomTap)))
return imageView
}()
func handleZoomTap(tapGesture: UITapGestureRecognizer) {
print("thisisatest")
}

Related

Swift ios how to set nav bar image not stretch

I have made it possible to add a background image to a nav bar using the ff code but the image is stretched. Is there any way that it will not be stretched?
self.navigationController?.navigationBar.setBackgroundImage(UIImage(named: "BG.jpg"), for: .default)
You could try this,
UINavigationBar.appearance().setBackgroundImage(UIImage(named: "BG.jpg")!.resizableImage(withCapInsets: UIEdgeInsets.zero, resizingMode: .stretch), for: .default)
Try out other options in the code as well.
UPDATE
You can even try below category for UINavigationBar
extension UINavigationController {
func setBackgroundImage(_ image: UIImage) {
navigationBar.isTranslucent = true
navigationBar.barStyle = .blackTranslucent
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
view.insertSubview(imageView, belowSubview: navigationBar)
NSLayoutConstraint.activate([
imageView.leftAnchor.constraint(equalTo: view.leftAnchor),
imageView.rightAnchor.constraint(equalTo: view.rightAnchor),
imageView.topAnchor.constraint(equalTo: view.topAnchor),
imageView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor)
])
}
}
Above code may be vary depending on your final requirements.
class TutorialViewController: UIViewController {
// This property will set the Image to Navigation Bar
public var navigationBarImage: UIImage? {
get {
return (navigationItem.titleView as? UIImageView)?.image
} set {
if newValue != nil {
let imageView = UIImageView(image: newValue)
imageView.contentMode = .scaleAspectFill
navigationItem.titleView = imageView
} else {
navigationItem.titleView = nil
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
navigationBarImage = #imageLiteral(resourceName: "my_nav_image")
}
for all images you can set the content mode =
.scaleToFill (UIViewContentModeScaleToFill)
.scaleAspectFit (UIViewContentModeScaleAspectFit)
.scaleAspectFill (UIViewContentModeScaleAspectFill)
If you do not want to see the contents that are outside the bounds you need to remember to set clipsToBounds to true on the containing superview
View.clipsToBounds = true

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

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.

Adding Tap Gesture to any view triggers nothing

I am dealing with the single most frustrating issue so far in my journey to iOS development. I originally was trying to add a gesture recognizer to my UIImage view but was not having any luck. I did some searching on stack and found that I hadn't set imageView.isUserInteractionEnabled = true which I figured would solve my problem but it didn't. So then I started adding gesture recognizers to everything including the parent view of imageView but still got nothing. I am sure that something I am doing/haven't done is so simple but I have just totally missed it. Please help.
ProfileViewController: UIViewController {
// User's info container (Parent iof imageView)
let userInfoContainer: UIView = {
let head = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
head.backgroundColor = UIColor.white
head.translatesAutoresizingMaskIntoConstraints = false
head.layer.borderWidth = 1
print(head.isUserInteractionEnabled) // Returns true
let tap = UITapGestureRecognizer(target: self, action: #selector(tapped))
head.addGestureRecognizer(tap)
return head
}()
// Users name
let userName: UITextField = {
let name = UITextField()
name.text = "John Doe"
name.translatesAutoresizingMaskIntoConstraints = false
name.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(tapped))
name.addGestureRecognizer(tap)
return name
}()
// User's profile image
let profileImageView: UIImageView = {
let imageView = UIImageView(image: #imageLiteral(resourceName: "avatar"))
imageView.layer.cornerRadius = 50
imageView.layer.masksToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapped)))
return imageView
}()
//
//
// skipping other UI elements
//
//
override func viewDidLoad() {
super.viewDidLoad()
// Authentication bits
self.title = "Profile View"
self.view.backgroundColor = UIColor.white
self.view.addSubview(userInfoContainer)
self.view.addSubview(profileImageView)
self.view.addSubview(userName)
} // end viewDidLoad
func tapped() {
print(123)
}
// setup constraints
func setupUserInfoContainer() {
userInfoContainer.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
userInfoContainer.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
userInfoContainer.heightAnchor.constraint(equalToConstant: 200).isActive = true
}
// profile image arrangement
func setupProfileImage() {
profileImageView.leftAnchor.constraint(equalTo: userInfoContainer.leftAnchor, constant: 20).isActive = true
profileImageView.centerYAnchor.constraint(equalTo: userInfoContainer.centerYAnchor).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 100).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 100).isActive = true
}
func setupLabels() {
userName.leftAnchor.constraint(equalTo: profileImageView.rightAnchor, constant: 20).isActive = true
userName.centerYAnchor.constraint(equalTo: profileImageView.centerYAnchor).isActive = true
userName.heightAnchor.constraint(equalToConstant: 20).isActive = true
userName.isEnabled = false
}
} // end view controller
Rendered View:
View Hierarchy
Extra information that is likely not necessary: ProfileViewController is being handled by a UITabBarController but from what I have seen, that shouldn't make a difference.
Update
From looking around it looks like the appropriate swift 3 syntax is
let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapped(_:))) but I think having that line in the closure is what's causing this error to be thrown ../ProfileViewController.swift:37:74: Value of type '(NSObject) -> () -> ProfileViewController' has no member 'tapped'
If someone could explain how I can fix this that would be stellar.
I don't believe you can assign a gesture recognizer in this manner...
1 let profileImageView: UIImageView = {
2 let imageView = UIImageView(image: #imageLiteral(resourceName: "avatar"))
3 imageView.layer.cornerRadius = 50
4 imageView.layer.masksToBounds = true
5 imageView.translatesAutoresizingMaskIntoConstraints = false
6 imageView.isUserInteractionEnabled = true
7 imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapped)))
8 return imageView
9 }()
On line 7, what is self? It is the function that is returning a UIImageView.
Your func tapped() is not part of that function... it belongs to ProfileViewController.
You might be able to find a way to change the target, but a couple minutes of trying different approaches has not yielded any luck for me.
I think you need to create and add the GestureRecognizer(s) inside ProfileViewController / viewDidLoad() (or elsewhere within that class).
The problem is you didn't set delegate to the tap gesture. Check this
Hope this will work
Why don't you just initiliaise your image view at the top of the class like
let profileImageView = UIImageView()
and create a function to configure the imageview and add it as a subview such in view did load. That should work
func configureImageView() {
#add methods and customize image view
#add image view to sub view
}

Why can't I return an array of objects to UIStackView in Swift? [duplicate]

This question already has answers here:
How to initialize properties that depend on each other
(4 answers)
Closed 5 years ago.
import UIKit
class MenuBar: UIView{
let buttonWidth = 50
let friendsButton: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = #imageLiteral(resourceName: "Friends Button")
imageView.contentMode = .scaleAspectFill
imageView.widthAnchor.constraint(equalToConstant: 50)
imageView.heightAnchor.constraint(equalToConstant: 50)
return imageView
}()
creating a friends button
let circleButton: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = #imageLiteral(resourceName: "Small Circle Button")
imageView.contentMode = .scaleAspectFill
imageView.widthAnchor.constraint(equalToConstant: 50)
imageView.heightAnchor.constraint(equalToConstant: 50)
return imageView
}()
creating a circle button
let profileButton: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = #imageLiteral(resourceName: "Profile Button")
imageView.contentMode = .scaleAspectFill
imageView.widthAnchor.constraint(equalToConstant: 50)
imageView.heightAnchor.constraint(equalToConstant: 50)
return imageView
}()
creating a profile button
let stackView = UIStackView(arrangedSubviews: [profileButton, circleButton, profileButton])}
The last line is the line that gives me the error: cannot use instance member 'profileButton within property initializer
The important part of the error message you got is the second part which unfortunately you omitted. It is property initializer is initialized before self.
The next error you'll get after rewrite without property initalizer pattern would be Expected declaration. That means the code segment need to be inside a function. Like viewDidLoad() for example.
Thus, for it to work, your code need to be modified to something similar to:
import UIKit
class MenuBar: UIView {
func createStackView() -> UIStackView {
let buttonWidth = 50
let friendsButton = UIImageView()
friendsButton.translatesAutoresizingMaskIntoConstraints = false
friendsButton.image = #imageLiteral(resourceName: "Friends Button")
friendsButton.contentMode = .scaleAspectFill
friendsButton.widthAnchor.constraint(equalToConstant: 50)
friendsButton.heightAnchor.constraint(equalToConstant: 50)
let circleButton = UIImageView()
circleButton.translatesAutoresizingMaskIntoConstraints = false
circleButton.image = #imageLiteral(resourceName: "Small Circle Button")
circleButton.contentMode = .scaleAspectFill
circleButton.widthAnchor.constraint(equalToConstant: 50)
circleButton.heightAnchor.constraint(equalToConstant: 50)
let profileButton = UIImageView()
profileButton.translatesAutoresizingMaskIntoConstraints = false
profileButton.image = #imageLiteral(resourceName: "Profile Button")
profileButton.contentMode = .scaleAspectFill
profileButton.widthAnchor.constraint(equalToConstant: 50)
profileButton.heightAnchor.constraint(equalToConstant: 50)
let stackView = UIStackView(arrangedSubviews: [profileButton, circleButton, profileButton])
return stackView
}
}

Resources