Swift: addGestureRecognizer not work for stackview children - swift

Codes:
for ... {
let view = CategoryClass.createMyClassView()
view.myLabel.text = packTitle
view.twoLabel.text = packText
view.bgCaategory.layer.cornerRadius = 30
i = i + 1
if(i == 1){
selectPackId = packId!;
view.imgSelect.image = UIImage(named: "selected")
} else {
view.imgSelect.image = UIImage(named: "select")
}
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleSendData(sender:))))
self.stackView.addArrangedSubview(view)
}
#objc func handleSendData(sender: UITapGestureRecognizer) {
print("H 1")
}
If i click on view, nothing print "H 1"
I want if i click on view, get id or another value of view

If adding isUserInteractionEnabled as suggested by Marcel still doesn't work, also make sure that every parent view in the hierarchy has a valid frame (you can check it in Debug View Hierarchy).
E.g. it happened to me to add a UIStackView into a parent UIView but the layout constraints were not correct, so I ended up having the parent UIView frame size as 0 (but the UIStackView was still visible).

If you create the UIStackView in interface builder, the isUserInteractionEnabled property is false by default. This means that the view and all it's child views won't respond to user interaction.
When you create a view in code, this property is true be default.
Add:
stackView.isUserInteractionEnabled = true
You only have to add this once, in your viewDidLoad for example.

The reason it doesn’t work is possibly a wrong method signature. The correct signature for recognizer actions is this:
recognizerAction(_ recognizer: UIGestureRecognizer)

Related

Removing one subview from superview in swift somehow makes another view invisible

Can someone help me understand why removing a subview from superview somehow makes another subview in the superview disappear visually? Code is shown below:
private func setupSubviews() {
...
...
self.view.addSubview(self.feedView) feedView
self.view.addSubview(self.bannerView)
}
private func setupConstraints() {
self.view.backgroundColor = .white
self.feedView.backgroundColor = .white
self.feedView.topAnchor.constraint(equalTo:
self.view.safeAreaLayoutGuide.topAnchor).isActive = true
self.bannerView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
constrain(self.feedView, self.view, self.bannerView) { feedView, container, banner in
feedView.left == container.left
feedView.right == container.right
feedView.bottom == banner.top
banner.left == container.left
banner.right == container.right
}
for view in self.view.subviews {
print("View: ", view)
//self.feedView.removeFromSuperview()
if view is DFPBannerView {
print(self.bannerView.superview)
self.bannerView.removeFromSuperview()
}
}
for view in self.view.subviews {
print("&&&***", view) //show the feedViews is still here
print(view.isHidden) //shows false
}
In the setupSubviews, I've added to subviews, feeView and bannerView. I need to remove the bannerView from the superview but for whatever reason when self.bannerView.removeFromSuperview() runs, the feedView also disappear visually. It's actually still in the self.views.subviews as I tried to see if it's still there but for some reason, nothing shows up other than just the white background.
If however I remove the feedView instead, the bannerView still shows up visually.
I naively thought perhaps somehow the feedView's isHidden property got set to true so I tried self.feedView.isHidden = false, but that didn't do anything.
So my question is what am I missing?

Bad layout constraints when adding accessory view to Open/Save dialog

I'm trying to add a simple NSView with a checkbox as an accessory view to an NSOpenPanel, but when I run my program, I get an error saying The Open/Save panel was supplied an accessory view with bad layout constraints, resulting in a view that is zero [height/width]. Here are the constraints I've added to the view:
And here are the constraints for the checkbox:
Here's the code for creating the NSOpenPanel:
let dlgOpenSounds: NSOpenPanel = NSOpenPanel()
let optionsView = BatchAddOptionsView()
dlgOpenSounds.accessoryView = optionsView
dlgOpenSounds.accessoryView?.awakeFromNib()
let result = dlgOpenSounds.runModal()
if result == .OK {
// do stuff
}
Anyone know what I'm doing wrong?
I ran into the same issue with a similar arrangement created in code, and finally worked it out. My implementation is handled in a custom NSView subclass, which I then add as the NSOpenPanel's .accessoryView from the view controller where I display the panel.
private func setup() {
hiddenFilesCheckbox = NSButton(checkboxWithTitle: "Show Hidden Files", target: self, action: #selector(hiddenFilesCheckboxValueChanged))
guard let checkbox = hiddenFilesCheckbox else {
os_log("Hidden files checkbox is nil")
return
}
addSubview(checkbox)
checkbox.translatesAutoresizingMaskIntoConstraints = false
checkbox.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 12).isActive = true
checkbox.topAnchor.constraint(equalTo: self.topAnchor, constant: 12).isActive = true
self.heightAnchor.constraint(greaterThanOrEqualToConstant: frame.height).isActive = true
self.widthAnchor.constraint(greaterThanOrEqualToConstant: frame.width).isActive = true
}
"hiddenFilesCheckbox" is declared as a property of my custom NSView subclass. I played around with some other hard-coded values for the constants, but these worked best in my tests. I pass in the openPanel to the subclass's initializer to use its frame to set the accessoryView's width. I used a hard-code value of 40 for the height in the initializer that isn't included here. After setting up the accessory view with these constraints, the warnings stopped appearing and the accessory view appears as desired/expected.
Try setting up the view like this (Xcode 10.1). First make sure that AutoLayout on the view is not selected. Then:
Change the view width and height to whatever is appropriate (I'm using a 'small' control size)
Setup the checkbox similar to:
Again, adjust the width and height as necessary. No other constraints should be added.
Note that if you save and reuse the accessory view in multiple panel.beginModalSheet() calls, you'll get a console warning because the previous beginModalSheet() added layout constraints.

How do I pass view as an argument (swift)

I'm trying to move my fully functioning swipe gesture code into a view model to clean up the view controller but the code uses a lot of self and view references, so I suppose I need to pass along the view or UIView.self as an argument when calling the function. Can't get it to work though. Tried:
vm.swipeCode(myView: self.view)
func swipeCode(myView: UIView) {...
But it crashes. After some research I also tried variations of inout and & but to no avail. Here's the full swipe code (it references back to the view controller but I will move those as well when things start working :) )
var myVC = RecipesViewController()
func swipeCode(myView: UIView) {
//SWIPE RIGHT
let swipingRight = UISwipeGestureRecognizer()
swipingRight.addTarget(self, action: #selector(myVC.swipeRight))
swipingRight.direction = .right
swipingRight.delegate = self as? UIGestureRecognizerDelegate
swipingRight.cancelsTouchesInView = false
myView.isUserInteractionEnabled = true
myView.addGestureRecognizer(swipingRight)
//// ALLOW SWIPE LEFT ////
let swipingLeft = UISwipeGestureRecognizer()
swipingLeft.addTarget(self, action: #selector(myVC.swipeLeft))
swipingLeft.direction = .left
swipingLeft.delegate = self as? UIGestureRecognizerDelegate
swipingLeft.cancelsTouchesInView = false
myView.isUserInteractionEnabled = true
myView.addGestureRecognizer(swipingLeft)
}
The crash is probably because of these two lines:
swipingRight.addTarget(self, action: #selector(myVC.swipeRight))
swipingLeft.addTarget(self, action: #selector(myVC.swipeLeft))
You passed myVC.swipeLeft as the action, but self as the target, so the gesture recogniser will try to find a swipeLeft method in self, which doesn't exist.
You should always ensure that the action is a member of the target:
swipingRight.addTarget(myVC, action: #selector(myVC.swipeRight))
swipingLeft.addTarget(myVC, action: #selector(myVC.swipeLeft))
You should simply make a UIView or UIViewController inherited class instead if your methods use a lot of self then simply make your controllers or views of that class.
class CustomViewController:UIViewController {
func swipeCode(myView: UIView) {
// Your code here
}
// Any other methods your code might need
}
class RecipesViewController:CustomViewController {
// Whatever else your view controller is made from
}
Or if your code swiping works in every view controller you could also extends the UIViewController to add those functionnalities

How to Disable Multi Touch on UIBarButtonItems & UI Buttons?

My app has quite a few buttons on each screen as well as a UIBarButtonItem back button and I have problems with people being able to multi click buttons. I need only 1 button to be clickable at a time.
Does anyone know how to make a UIBarButtonItem back button exclusive to touch?
I've managed to disable multi clicking the UIButtons by setting each one's view to isExclusiveTouch = true but this doesn't seem to count for the back button in the navigation bar.
The back button doesn't seem to adhere to isExclusiveTouch.
Does anyone have a simple work around that doesn't involve coding each and every buttons send events?
Many Thanks,
Krivvenz.
you can enable exclusive touch simply this will stop multiple touch until first touch is not done
buttton.exclusiveTouch = true
You could write an extension for UIBarButtonItem to add isExclusiveTouch?
you can simply disable the multi-touch property of the super view. You can also find this property in the storyboard.
you could try this for the scene where you want to disable multiple touch.
let skView = self.view as! SKView
skView.isMultipleTouchEnabled = false
I have found a solution to this. isExclusiveTouch is the solution in the end, but the reason why this property didn't do anything is because you need to set it also on all of the subviews of each button that you want to set as isExclusiveTouch = true. Then it works as expected :)
It's working fine in Swift
self.view.isMultipleTouchEnabled = false
buttonHistory.isExclusiveTouch = true
In addition of #Luky Lízal answer, you need pay attention that all subviews of a view you want disable multi-touching (exactly all in hierarchy, actually subviews of subviews) must be set as isExclusiveTouch = true.
You can run through all of them recursively like that:
extension UIView
{
func allSubViews() -> [UIView] {
var all: [UIView] = []
func getSubview(view: UIView) {
all.append(view)
guard view.subviews.count > 0 else { return }
view.subviews.forEach{ getSubview(view: $0) }
}
getSubview(view: self)
return all
}
}
// Call this method when all views in your parent view were set
func disableMultipleTouching() {
self.isMultipleTouchEnabled = false
self.allSubViews().forEach { $0.isExclusiveTouch = true }
}

TapGestureRecognizer not firing in swift

Hi I'm having no cards to used to fixed this. Here we go.
I have this five imageview this is the declaration of first imageview but basically they have same declaration
#IBOutlet weak var first: UIImageView!{
didSet{
first.tag = 1
first.addGestureRecognizer(tapGesture())
}
}
and Then the tapGesture() method is this
private func tapGesture() -> UITapGestureRecognizer {
let tapGesture = UITapGestureRecognizer(target: self, action: Selector("iconTapped:"))
tapGesture.numberOfTapsRequired = 1
tapGesture.numberOfTouchesRequired = 1
return tapGesture
}
the action when the imageview is tapped the iconTapped() method is called, this is the code
func iconTapped(sender:UITapGestureRecognizer){
if let tag = sender.view?.tag{
processGesture(tag)
}
}
I have put all imageview userInteractionEnable to true but still not firing. Could anyone help me?
Some factor may or may not help, inside the viewdidload I update constraints and do some animation to imageview, but I'm not adding any subview programmatically
after trying so many things, I look on my view hierarchy and figure out that the parent view where my UIImageView rest userInteractionEnable is set to false(unchecked) in storyboard.
in this case the parentview is my superview, to fixed that I need to add this in viewDidLoad()
self.view.userInteractionEnabled = true
or
click the parentView in storyboard and in attributes inspector check the User Interaction Enable.
ps. sorry for silly mistake but I'll keep this question posted there might be other encountering same problem.