Unable to click on Textfield on GMaps for iOS Subview - ios

I would like to add a searchbar (like in the Google Maps App for iOS).
So to look like the same as in that App, ill created a UITextField.
For example:
override func viewDidLoad() {
/* Add Google Map */
let camera = GMSCameraPosition.cameraWithLatitude(49.077872,longitude: 19.450339, zoom: 17)
mapView = GMSMapView.mapWithFrame(CGRectZero, camera: camera)
self.view = mapView
mapView.delegate = self
mapView.indoorDisplay.delegate = self
mapView.myLocationEnabled = true
mapView.settings.myLocationButton = true
mapView.padding = UIEdgeInsetsMake(64, 0, 64, 0)
mapView.setMinZoom(15, maxZoom: 19)
// add Searchbar
let screenWidth = UIScreen.mainScreen().bounds.width
searchTextField = UITextField(frame: CGRectMake(16, 50, screenWidth - 32, 40))
searchTextField.delegate = self
searchTextField.placeholder = "Gebäude und Räume suchen"
searchTextField.backgroundColor = UIColor.whiteColor()
searchTextField.clearButtonMode = UITextFieldViewMode.WhileEditing
searchTextField.layer.borderWidth = 1
searchTextField.layer.borderColor = UIColor.customGrey().CGColor
searchTextField.font = UIFont.systemFontOfSize(16.0)
let showCategories = UIButton(type: .Custom)
showCategories.frame = CGRectMake(0, 0, 40, 40)
showCategories.setTitle(String.fontAwesomeIconWithCode("fa-calendar-plus-o"), forState: .Normal)
showCategories.setTitleColor(UIColor.customDarkGrey(), forState: .Normal)
showCategories.titleLabel?.font = UIFont.fontAwesomeOfSize(20)
showCategories.addTarget(self,action: "calendarAddButtonPressed",forControlEvents: UIControlEvents.TouchUpInside)
searchTextField.rightView = showCategories
searchTextField.rightViewMode = .Always
searchTextField.userInteractionEnabled = true
self.mapView.addSubview(searchTextField)
The Button works fine, but i am unable to focus on the TextField.
When ill set (in ViewDidLoad).
searchTextField.becomesFirstResponder()
The Keyboard is there.
Also that event:
func textFieldDidBeginEditing(textField: UITextField) {
print("start searching")
self.becomeFirstResponder()
}
Is not fireing.
Any ideas? When the SubView is not on the top level - the button should not work too, or?

The accepted answer didn't help me at all, but it turns out there's another question on SO that is similar, but with Objective C.
All GMSMapViews have a GMSBlockingGestureRecognizer by default, and that is why your textField isn't working the way you want it to. The solution is to remove it, so you can put this in your viewDidLoad() method:
Swift 3
for gesture in mapView.gestureRecognizers! {
mapView.removeGestureRecognizer(gesture)
}
Alternatively, if you for some reason added other gesture recognizers to the mapView, you can do this:
for (index,gesture) in mapView.gestureRecognizers!.enumerated() {
if index == 0 {
mapView.removeGestureRecognizer(gesture)
}
}
Since the GMSBlockingGestureRecognizer is added by default, it will always be the first one in the array of gesture recognizers.
Also, make sure you put the for loop somewhere it will only be called once because if it is called multiple times, you could remove other gestures unintentionally.

Ok ill found a solution for someone who is struggling with the same problems.
When you set:
self.view = mapView
It looks like that you are unable to set ANY subviews, because that Views never receive touch events.
So in that case use:
mapView = GMSMapView.mapWithFrame(self.view.bounds, camera: camera)
self.view.addSubview(mapView)
Then all SubViews work fine.

Related

Making NavigationBar subview clickable in Swift

I have a View Controller embedded in Navigation Controller. The view has 1 WKWebView, hence, I'm setting view = webView in loadView() override.
So, I'm adding a small little sub navigation bar underneath my navigation controller to allow a user to change their location.I can add the subview to the navigation controller, I'm just not able to make it clickable.
override func loadView() {
let config = WKWebViewConfiguration()
config.processPool = YourModelObject.sharedInstance.processPool
webView = WKWebView(frame: .zero, configuration: config)
webView.navigationDelegate = self
self.webView.scrollView.delegate = self
view = webView
..
if let navigationBar = self.navigationController?.navigationBar {
let secondFrame = CGRect(x: 50, y: 44.1, width: navigationBar.frame.width, height: 30)
let secondLabel = UILabel(frame: secondFrame)
secondLabel.textColor = .black
secondLabel.text = "Getting your location..."
secondLabel.isUserInteractionEnabled = true
let guestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(setLocation(_:)))
secondLabel.addGestureRecognizer(guestureRecognizer)
secondLabel.textAlignment = .left
secondLabel.font = secondLabel.font.withSize(14)
secondLabel.tag = 1002
navigationBar.addSubview(secondLabel)
}
}
And then the setLocation function
#objc func setLocation(_ sender: Any) {
print("location label tapped")
}
But when I tap the label, I'm not getting anything printed in console. I don't know if the use of target: self is wrong for the tapGestureRecognizer or what's going on here.
I too am new to Swift, so my answer is far from guaranteed. I just know what it's like to be in your position,
Perhaps try creating a subclass of navigationBar for the sub navigation bar, i.e. mySubNavigationBar. Then in the subclass's code do all the initialization that you need to do. Including the print line so you'll know if you're getting there.
p.s. I would have put this as a comment, but I don't have enough points to add comments.

Center UILabel created in code using Swift

This may be the simplest thing you can possibly due in Xcode in Swift and for some reason, it is not working properly.
I want to center a label in a view. The only other thing in the view previously was a webView added programatically but for now I have removed that so basically, I have an empty VC in which I'm trying to center a label.
There are umpteen answers on SO about this and I've tried every combination but can't get it to to work.
Can anyone suggest a foolproof way to accomplish the simple task of centering a UILabel?
Below is the code I currently have and steps I've taken along with result:
I created an empty view controller in Storyboard and embedded it in a navigation controller.
I set the View Controller in Storyboard to my swift VC class. I also have already cleaned project, closed and re-opened XCode and also deleted storyboard and recreated it in case it was corrupted. Still nothing works.
myVC.swift
import UIKit
class myVC: UIViewController,WKScriptMessageHandler, WKNavigationDelegate,WKUIDelegate {
var title= "Hello there"
var loadingLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
webView.navigationDelegate = self
webView.uiDelegate = self
loadingLabel.translatesAutoresizingMaskIntoConstraints = false
// loadingLabel.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
// loadingLabel.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
// loadingLabel = UILabel(frame: CGRect(x: 0, y: self.view.center.y, width: 290, height: 70))
loadingLabel.center = self.view.center
loadingLabel.textAlignment = .center
loadingLabel.font = UIFont(name: "Halvetica", size: 18.0)
loadingLabel.numberOfLines = 0
loadingLabel.text = "TEXT I WANT TO CENTER"
loadingLabel.lineBreakMode = .byTruncatingTail
loadingLabel.center = self.view.center
self.view.addSubview(loadingLabel)
self.title = title
}
override func loadView() {
super.loadView()
}
}
Add the loadingLabel as subview before adding the constraints.
view.addSubview(loadingLabel)
loadingLabel.translatesAutoresizingMaskIntoConstraints = false
loadingLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
loadingLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

why does a custom button which added by code in UITabBarController.viewDidLoad doesn't response the selector

I add a custom button to the tabBar in my MyViewController.viewDidLoad(subclass of UITabBarController)
But I find it doesn't response the selector.
If I delay one second to add button(in DispatchQueue.main.asyncAfter closure) ,it works OK.
I think it's not the right way to resolve it.
func addButton() {
let button = UIButton(type: UIButton.ButtonType.custom)
button.bounds = CGRect(x:0,y:0,width:30,height:30);
button.backgroundColor = UIColor.red
button.center = CGPoint(x:self.tabBar.frame.size.width/2, y:self.tabBar.frame.size.height/2 - 20);
button.addTarget(self, action: #selector(click(button:)), for: UIControl.Event.touchUpInside)
tabBar.addSubview(button)
}
You have added button to UITabBar of UITabBarController as half of part of the button would appear above the Tabbar and half of below the Tabbar as per frame.
So I guess you will not get click on part of that button which is out of Tabbar(above Tabbar) would not get touch. I you will make button little big OR try to click with arrow in simulator, you will get idea.
If you need to have button at bottom but slightly upper, then please create custom Tabbar to achieve design like this. Or else you can add that button into UITabBarController’s view instead of Tabbar.
class MyTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.addButton()
}
func addButton() {
let button = UIButton(type: UIButton.ButtonType.custom)
button.bounds = CGRect(x:0,y:0,width:50,height:50); //1
button.backgroundColor = UIColor.purple
button.center = CGPoint(x:self.tabBar.frame.size.width/2, y:self.tabBar.frame.size.height/2 - 50 + self.tabBar.frame.origin.y); //2
button.addTarget(self, action: #selector(click(button:)), for: UIControl.Event.touchUpInside)
button.layer.cornerRadius = button.frame.size.height/2
button.layer.masksToBounds = false
button.layer.shadowColor = UIColor.black.withAlphaComponent(0.5).cgColor
button.layer.shadowRadius = 5.0
button.layer.shadowOffset = CGSize(width: 0.0, height: 5.0)
button.layer.shadowOpacity = 0.5
//tabBar.addSubview(button) //3
self.view.addSubview(button). //4
}
#objc func click(button: UIButton) {
print("Button get clicked")
}
}
I have marked four things with commented by numbers at the end of lines, that you can make to your code and try.

UIStackView & Gestures

I'm trying to get/keep a handle on elements in my UIStackView after I have moved it with a pan gesture.
For example, the elements I am trying to grab a handle on are things like the button tag or the text label text.
The code explained…
I am creating a UIStackView, via the function func createButtonStack(label: String, btnTag: Int) -> UIStackView
It contains a button and a text label.
When the button stack is created, I attach a pan gesture to it so I can move the button around the screen. The following 3 points work.
I get the button stack created
I can press the button and call the fun to print my message.
I can move the button stack.
The issue I have is…
Once I move the button stack the first time and the if statement gesture.type == .ended line is triggered, I lose control of the button stack.
That is, the button presses no longer work nor can I move it around any longer.
Can anyone please help? Thanks
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .lightGray
let ButtonStack = createButtonStack(label: “Button One”, btnTag: 1)
view.addSubview(ButtonStack)
ButtonStack.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
ButtonStack.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
let panGuesture = UIPanGestureRecognizer(target: self, action: #selector(pan(guesture:)))
ButtonStack.isUserInteractionEnabled = true
ButtonStack.addGestureRecognizer(panGuesture)
}
func createButtonStack(label: String, btnTag: Int) -> UIStackView {
let button = UIButton()
button.setImage( imageLiteral(resourceName: "star-in-circle"), for: .normal)
button.heightAnchor.constraint(equalToConstant: 100.0).isActive = true
button.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
button.contentMode = .scaleAspectFit
button.tag = btnTag
switch btnTag {
case 1:
button.addTarget(self, action: #selector(printMessage), for: .touchUpInside)
case 2:
break
default:
break
}
//Text Label
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.green
textLabel.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 25.0).isActive = true
textLabel.font = textLabel.font.withSize(15)
textLabel.text = label
textLabel.textAlignment = .center
//Stack View
let buttonStack = UIStackView()
buttonStack.axis = UILayoutConstraintAxis.vertical
buttonStack.distribution = UIStackViewDistribution.equalSpacing
buttonStack.alignment = UIStackViewAlignment.center
buttonStack.spacing = 1.0
buttonStack.addArrangedSubview(button)
buttonStack.addArrangedSubview(textLabel)
buttonStack.translatesAutoresizingMaskIntoConstraints = false
return buttonStack
}
#objc func printMessage() {
print(“Button One was pressed”)
}
#objc func pan(guesture: UIPanGestureRecognizer) {
let translation = guesture.translation(in: self.view)
if let guestureView = guesture.view {
guestureView.center = CGPoint(x: guestureView.center.x + translation.x, y: guestureView.center.y + translation.y)
if guesture.state == .ended {
print("Guesture Center - Ended = \(guestureView.center)")
}
}
guesture.setTranslation(CGPoint.zero, in: self.view)
}
If you're using autolayout on the buttonStack you can't manipulate the guestureView.center centerX directly. You have to work with the constraints to achieve the drag effect.
So instead of ButtonStack.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true you should do something along the lines of:
let centerXConstraint = ButtonStack.centerXAnchor.constraint(equalTo: self.view.centerXAnchor)
centerXConstraint.isActive = true
ButtonStack.centerXConstraint = centerXConstraint
To do it like this you should declare a weak property of type NSLayoutConstraint on the ButtonStack class. You can do the same thing for the centerY constraint.
After that in the func pan(guesture: UIPanGestureRecognizer) method you can manipulate the centerXConstraint and centerYConstraint properties directly on the ButtonStack view.
Also, I see you are not setting the translatesAutoresizingMaskIntoConstraints property to false on the ButtonStack. You should do that whenever you are using autolayout programatically.
Thanks to PGDev and marosoaie for their input. Both provided insight for me to figure this one out.
My code worked with just the one button, but my project had three buttons inside a UIStackView.
Once I moved one button, it effectively broke the UIStackView and I lost control over the moved button.
The fix here was to take the three buttons out of the UIStackView and I can now move and control all three buttons without issues.
As for keeping a handle on the button / text field UIStackView, this was achieved by adding a .tag to the UIStackView.
Once I moved the element, the .ended action of the pan could access the .tag and therefore allow me to identify which button stack was moved.
Thanks again for all of the input.

SWIFT Removing Subviews

I'm trying to have the ability to remove the subview the user chooses with three taps.
The problem I'm having is I can only get it to remove in the order the subviews were created. I have played around with 'viewWithTag' but can't figure out how to get it to do what I want.
Can I achieve what I want with the 'tapGesture' removing the subview from the location I tapped?
I'm a noob (as the kids say), so any help is much appreciated!
Thanks!
#IBAction func unwindToParent(segue:UIStoryboardSegue){
var source = segue.sourceViewController as PropViewController
var propImage = UIImage(named: name as String!)
clipView = UIImageView(image: propImage!)
clipView.frame = CGRectMake(0, 0, 200.0, 200.0)
clipView.center = CGPoint (x: view.bounds.size.width/2, y: view.bounds.size.height/2)
clipView.contentMode = UIViewContentMode.ScaleAspectFit
clipView.userInteractionEnabled = true
clipView.multipleTouchEnabled = true
clipView.layer.cornerRadius = 10.0
setTag = tagCounter
tagCounter++
clipView.tag = setTag
addPinchGestureRecognizer(clipView)
addPanGestureRecognizer(clipView)
addRotationGestureRecognizer(clipView)
addTapGestureRecognizer(clipView)
view.addSubview(clipView)
view.bringSubviewToFront(clipView)
let recognizer = UITapGestureRecognizer(target: self, action:Selector("Trash:"))
recognizer.numberOfTapsRequired = 3
recognizer.delegate = self
clipView.addGestureRecognizer(recognizer)
}
func Trash(gesture: UITapGestureRecognizer){
clipView.viewWithTag(setTag)?.removeFromSuperview()
}
The gesture recogniser has a view property - that will be the tapped view.
Never use tags for anything.
Theres probably a half dozen ways I can see this being done.
The 'simplest' is probably getting the location of the tap
tap.locationInSubview
And then checking if any of the views contain that point
uiViewObject.containtsPoint()
If it contains the point remove it
Or you could add gestures to each view and, as suggested by jrturton, uses the taps view source as the view to remove.

Resources