Swift - Custom SearchController and SearchBar - ios

I am trying to recreate the search field as seen in the Yahoo Finance app. I followed an online tutorial for customizing the UISearchBar and UISearchController, however I still have some problems. If any of you could open up my project and see where Im going wrong / where I need to add these lines with even one of these that would be really great.
My attempted solution project can be found here: https://github.com/jordanw421/yahoofinance
1) How to get the search bar active (with text to the left, and typing indicator blinking) as soon as view presents itself? In the link below, you can see what I am talking about, when the button is pressed the search view is loaded and the search bar is instantly active.
https://youtu.be/tRtXm-m1hX0
I tried using:
customSearchController.definesPresentationContext = true
customSearchController.isActive = true
customSearchController.searchBar.becomeFirstResponder()
but that didn't work. Should I be setting these in the initial view controller with prepareForSegue?
2) How to set the keyboard appearance (to dark), and to add a keyboard toolbar with a button?
I was able to get this working for a non-custom search bar, but for some reason these don't work now:
customSearchController.searchBar.keyboardAppearance = .dark
and
func addKeyboardButton() {
let keyboardToolbar = UIToolbar()
keyboardToolbar.sizeToFit()
keyboardToolbar.isTranslucent = false
keyboardToolbar.barTintColor = UIColor.blue
let addButton = UIButton(type: .custom)
addButton.frame = CGRect(x: keyboardToolbar.frame.size.width / 2, y: keyboardToolbar.frame.size.height / 2, width: 50, height: 30)
addButton.addTarget(self, action: #selector(clickMe), for: .touchUpInside)
let item = UIBarButtonItem(customView: addButton)
keyboardToolbar.items = [item]
customSearchController.searchBar.inputAccessoryView = keyboardToolbar
}
and calling,
addKeyboardButton()
in the search bar configure function.
3) How to prevent the search bar / table view header from scrolling, but still allow the tableView to scroll?
If you look at my attempted solution you can see that for some reason the tableview header scrolls with the table view. When I use a non-custom search bar the header remains static.
I know there are a lot of questions here, but I've been stuck on this for awhile and could really use some help. Thank you.

Related

Add Button Tag Buttons inside SearchBar

I want to implement when I start editing inside search bar, then list would appear below the searchbar and I would select multiple values from list and when I want to select any text It would be add inside my search bar or textfield like button like this image below
this is example from stack overflow, so please guide me how to implement this I have no idea about this implementation, you can refer also third party library also and if you can help me with code. Thanks
may it will works
i suggested to use textfield as search engine
write code inside add button
let button = UIButton(type: .custom)
button.setTitle("name", for: UIControlState.normal)
button.frame = CGRect((x: CGFloat(txt.frame.size.width - 25), y: CGFloat(5), width: CGFloat(25), height: CGFloat(25)))
button.addTarget(self,action: #selector(action), for: .touchUpInside)
textField.rightView = button
textField.rightViewMode = .always
use array to save the values

UITextField is not editable, has only Touch Events, no Editing Events

I have a UITextField above a Google Maps View (GMSView). When I tap on the Textfield it does not become the first responder, neither do any of the delegate or action events work (Delegate Event like textFieldShouldBeginEditing and Action Events like .allEditingEvents).
The only way the Textfield is becoming the first responder is by hardcoding it in with
tf.becomeFirstResponder()
But when I tap on it nothing happens.
The Action Touch events like .touchUpInside etc. do work normally on the TextField, so I know the frame is ok.
I am creating and adding the TextView like this:
tf = UITextField(frame: CGRect(x: 100, y: 200, width: 300, height: 50))
tf.backgroundColor = .white
tf.placeholder = "sdfdsf"
tf.translatesAutoresizingMaskIntoConstraints = false
tf.addTarget(self, action: #selector(self.tapped), for: .touchUpInside)
self.mapView.addSubview(tf)
tf.centerXAnchor.constraint(equalTo: mapView.centerXAnchor).isActive = true
tf.centerYAnchor.constraint(equalTo: mapView.centerYAnchor).isActive = true
tf.widthAnchor.constraint(equalToConstant: 300).isActive = true
tf.heightAnchor.constraint(equalToConstant: 50).isActive = true
If you have any idea why this happens (or in this case does not happen) please leave a comment or an answer. I have worked on this for over 2 days straight now.
UPDATE:
I actually added the textfield to the Mapview itself. The Mapview
kinda does something strange with the layering of the view and messes
up the view of the TextView.
The solution was to add the TextView to the main view and not to the
MapView subview.
So it's (from back to front layer):
ViewController
(Main) View
Google Maps view
View
TextField
This works
Please try this:
self.mapView.bringSubviewToFront(tf) // Add this line below "self.mapView.addSubview(tf)"
This will ensure that the tf(TextField is on Top and Tappable)
Did you assigne delegate to text field like :-
tf. delegate = self
If you want to bring a subview to the front, you can use:
self.view.bringSubviewToFront(tf)

Subclassing UINavigationBar with Swift

I'm writing an app that gives a user tokens to spend and I want to display the user's current number of tokens in a UINavigationBar. What I would like to have is a label with the number of tokens, and an image of a coin in the top right corner of my navigation bar.
I've been searching for ways to customize the UINavigationBar, and have found plenty of posts related to adding an image to cover the entire bar, and changing the title. However, I can't find a simple way to do what I want.
I think I need to subclass UINavigationBar and add the text/image myself, but being new to iOS development and Swift, I was hoping that someone could point me in the right direction.
You can create a custom view contains subviews UILabel and UIImageView to show the token number and token image. Add it to right bar button item of the navigation controller.
It will look like:
Below code will create the custom view. Here you can observe that it is a local variable. However, you can manage global variable for custom view or create a whole new class and manage it independently for real-time updates to show token number.
// Custom to hold token number and image
let tokenView = UIView(frame: CGRect(x:0, y:0, width:100, height:44))
tokenView.backgroundColor = UIColor.yellow
// Label to show token number
let tokenLabel = UILabel(frame: CGRect(x:0, y:0, width:60, height:44))
tokenLabel.text = "1234"
tokenLabel.textAlignment = NSTextAlignment.right
let imageHeight = CGFloat(30)
let marginY = CGFloat((tokenView.frame.size.height / 2) - (imageHeight / 2))
// ImageView to display token image
let tokenImage = UIImageView(image: UIImage(named: "coin"))
tokenImage.frame = CGRect(x:70, y:marginY, width:30, height:30)
tokenView.addSubview(tokenLabel)
tokenView.addSubview(tokenImage)
// Add custom view as a right bar button item
let barButtonItem = UIBarButtonItem(customView: tokenView)
self.navigationItem.rightBarButtonItem = barButtonItem
To display the current number of tokens you can just set the title. In your view controller:
navigationItem.title = '\(numberOfCoins) coins"
If you want to do anything more fancy you can set your own title view instead of the standard UILabel:
navigationItem.titleView = UIView(...) // your custom view
To show the coin in the top right corner you'd set the rightBarButtonItem:
navigationItem.rightBarButtonItem = UIBarButtonItem(
image: UIImage(named:"Coin"),
style: .Plain,
target:self,
action:"tappedCoinButton:")

UISearchBar Scope Bar Position?

I am here on an iPad Application and i would like to know if its possible to move the Scope Bar from right to my UISearchBar to another position?
I would like to have my Scope Bar under my Search Bar. Is that possible?
Thanks in advance.
Ok this is my solution for that. Ill implemented my own segmented control to create a possibility for a search scope.
let categories = ["Scope1", "Scope2", "Scope3"]
segmentedControl.addTarget(self, action: "changeScope:", forControlEvents: .ValueChanged)
segmentedControl.frame = CGRectMake(8, 5, 800, 30)
segmentedControl.backgroundColor = UIColor.whiteColor()
segmentedControl.tintColor = UIColor.darkGrayColor()
// add it in a scrollView, because ill got too much categories here. Just if you need that:
scrollView.contentSize = CGSizeMake(segmentedControl.frame.size.width + 16, segmentedControl.frame.size.height-1)
scrollView.showsHorizontalScrollIndicator = false;
// set first category
segmentedControl.selectedSegmentIndex = 0
scrollView.addSubview(segmentedControl)
Here is the function for the scope bar, do wantever you want when a user switches a scope:
func changeScope(sender: UISegmentedControl) {
switch(sender.selectedSegmentIndex) {
}
}
In my case, ill got several resultArrays from an Webservice, and ill only show the selected result (normally ill Add them to 1 huge resultSet)
Hope that helps maybe someone else.

UIButton in Swift is not registering touches

I'm trying to create a UIButton using Swift. It compiles fine and I can see my button in the simulator, but when I click it, nothing happens. This is the code I am using:
let settings = UIButton()
settings.addTarget(self, action: "touchedSet:", forControlEvents: UIControlEvents.TouchUpInside)
settings.setTitle("Settings", forState: .Normal)
settings.frame = CGRectMake(0, 530, 150, 50)
scrollView.addSubview(settings)
In the same class, here is the function 'touchedSet':
func touchedSet(sender: UIButton!) {
println("You tapped the button")
}
I'm using the simulator as I don't have an iOS 8.0 device, could that be the problem?
Thanks!
I see it is an old question but I just faced very similar problem yesterday. My buttons were highlighted on touch but not firing the action.
I have two view controllers. One view covers the others view and button is on the top view.
eg.
Rootviewcontroller has back view
Topviewcontroller has top view
The button on top view does not call the action if I don't add the topviewcontroler as childviewcontroller of the rootviewcontroller. After adding the topviewcontroller as childviewcontroller it started to working.
So in short: just try to add the view controller of buttons superview as childviewcontroller to the parent views viewcontroller with the following method
func addChildViewController(_ childController: UIViewController)
Selectors are a struct that have to be created.
Do it this way...
Updated for Swift 4
settingsButton.addTarget(self, action: #selector(showSettings), for: .touchUpInside)
That should do it.
For anyone else doing manual view layout running into this issue, you might have defined your subview like such:
let settingsButton: UIButton = {
let button = UIButton(type: .system)
// do some more setup
button.addTarget(self, selector: #selector(openSettings), for: .touchUpInside)
return button
}()
The error lies with the fact that you are adding a target and passing self before it is actually available.
This can be fixed in two ways
Making the variable lazy and still adding the target in the initialization block:
lazy var button: UIButton = {
let button = UIButton(type: .system)
// since this variable is lazy, we are guaranteed that self is available
button.addTarget(self, selector: #selector(openSettings), for: .touchUpInside)
return button
}()
Adding the target after your parent view has been initialized:
init() {
self.settingsButton = .init(type: .system)
super.init(frame: .zero)
self.settingsButton.addTarget(self, selector: #selector(openSettings), for: .touchUpInside)
}
In the simulator, sometimes it doesn't recognise the selector. There is a bug it seems. I just changed the action name (selector), and it worked.
let buttonPuzzle:UIButton = UIButton(frame: CGRectMake(100, 400, 100, 50))
buttonPuzzle.backgroundColor = UIColor.greenColor()
buttonPuzzle.setTitle("Puzzle", forState: UIControlState.Normal)
buttonPuzzle.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
buttonPuzzle.tag = 22;
self.view.addSubview(buttonPuzzle)
Selector Function is Here:
func buttonAction(sender:UIButton!)
{
var btnsendtag:UIButton = sender
if btnsendtag.tag == 22 {
//println("Button tapped tag 22")
}
}
I have had this problem when parent view of my button has isUserInteractionEnabled == false. All subviews will have the same userInteraction as their parent view. So I have just added this line parentView.isUserInteractionEnabled = true and the problem disappeared. Hope this helps someone.
For those who are also stuck in the tutorial:
I ran in to the same problem, when I was following Apple's "Start Developing iOS Apps (Swift)". It turned out that I had overlooked these code lines in the tutorial:
override var intrinsicContentSize : CGSize {
return CGSize(width: 240, height: 44)
}
Adding them to my RatingControl fixed the problem.
I had a similar problem when i had a subViewController with buttons and tapHandlers to match, and I wanted to place this to a stack in a separate superViewController..
This cause none of the tapHandlers to to trigger, and the solution was to instead of only using addArrangedSubview(subViewController.view), I also used addChildViewController(subViewController) in the superview to allow the childViewController to continue to operate as a viewController and not just a view.
So you should use following line
settings.userInteractionEnabled = true
Old question, but thought I'd give some help to other looking for this issue. First off, I'm not entire sure this is correct so please correct me if I'm wrong.
But if you set the selector wrongly the application would crash when you press the button since it's subscribed to a selector which doesn't exist. Which means that your selector is working. My guess is that something else is blocking the touch event, assuming UIButton.enabled = true and UIButton.userInteractionEnabled = true. Usually you could check this by long pressing the button and see if it hits. If it does then you have a UIGestureRecognizer added somewhere which takes up the hits first. Could also be the UIScrollView which is delaying the touch input (there's a checkbox for this in the Inspector).
Hope this helps someone, and hope it's accurate.
I had the same issue when implementing UIButton and SearchBar on container view(UIView).
In my case, the cause was the constraint issue. Constraint to searchBar had issue and because of this, function set had never been called.
You can see if there's any from "Debug View Hierarchy" button. (constraint problem is shown as purple warning)
(env: iOS12, xcode10.1)
Interestingly enough, I just ran into the same issue on the very latest versions of iOS/Xcode (v12.4 / v10.3). Turns out the issue for me was a LayoutConstraint! No joke. I had a leading label with the uiSwitch to the right, and found that I needed to change the constraints between the two such that it wasn't a fixed constant value of 8 points (Label-8-Switch-0-|).
As soon as I changed this to a ">=" the Switch was able to change states between on/off. Laughably, it's almost like it wasn't able to change because it needs "room" (that apparently varies) to make the change with.
Not sure, but file it away as one of those "hummmm?" items in your memory.
One other item (that really shouldn't matter, frankly) is the valueChanged and tappedUpInside were both not firing (true on both an actual handset and on the simulators). Also, they were hooked up through a storyboard. But, this shouldn't matter as far as I know.
I had the same issue. The problem was the view had top constraint, but not left/right and height constraints. So, the view was shrinking to 1x1 and it was not passing the touch event to children.
By adding more constraints, the children now getting the touch event and working ...
let guide = view.safeAreaLayoutGuide
viewHeader.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
viewHeader.rightAnchor.constraint(equalTo: guide.rightAnchor).isActive = true
viewHeader.leftAnchor.constraint(equalTo: guide.leftAnchor).isActive = true
let heightConstraint = NSLayoutConstraint(item: viewHeader,
attribute: NSLayoutConstraint.Attribute.height,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: nil,
attribute: NSLayoutConstraint.Attribute.notAnAttribute,
multiplier: 1,
constant: 44)
viewHeader.addConstraints([heightConstraint])
Old question, but I also found myself stuck with an unresponding button. I simply changed my initialisation from
let button = UIButton(frame: .zero)
to
let button = UIButton(type: .system)
I can see only one problem: You have to set the action with Selector as following:
settings.addTarget(self, action: Selector("touchedSet:"), forControlEvents: UIControlEvents.TouchUpInside)
Don't forget to insert : if you need to pass parameters to the function.
You said within the same class. Make sure that the button code itself is in the viewDidLoad() and the action be outside of it but inside the class if you are working with a view.
The problem with that tutorial from Apple is the way they put the UIView (RatingControl) inside the StackView, which is incorrect. The UIView (RatingControl) should be outside of the StackView. So, the solution is:
1. Drag the UIView (RatingControl) outside of the StackView
2. Set the constraints for the new position of the RatingControl
- Leading margin to the Container equal zero
- Trailing margin to the Container equal zero
- Top margin to the StackView equal zero
With this new constraints, now the RatingControl will have the same with as the Container, no matter what is the device screen size, and always will be just behind of the StackView where the photo selector will appear.
I hope this help you guys.
Make sure you link you button with your IBOutlet instance variable in your code if you're using storyboard.
I've found similar example for Swift 2 and adapted for Swift 3. Tested it is working very well. I hope it helps.
// http://lab.dejaworks.com/ios-swift-3-playground-uibutton-action
// Trevor D. Beydag
import UIKit
import PlaygroundSupport
class Responder : NSObject {
func action() {
print("Button pressed!")
}
}
let containerView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 300.0, height: 600.0))
PlaygroundPage.current.liveView = containerView
let responder = Responder()
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
button.backgroundColor = UIColor.green
button.setTitle("TEST", for: .normal)
button.addTarget(responder, action: #selector(Responder.action), for: .touchUpInside)
containerView.addSubview(button)
I had changed the height and width constraints to 0, for some reason I needed to do this so it showed correctly on a multipurpose view for one use case, the buttons were visible but the touch stopped working.
In my case, I had a transparent UIView above the UIButton in the view hierarchy, which is why when I was "clicking on the UIButton", I was actually clicking on the transparent UIView on top of it.
Sending the transparent UIView back using parentUIView.sendSubviewToBack(transparentUIView) worked for me, because after doing this the UIButton came on top of the view hierarchy. Click on the "Debug View Hierarchy" button in Xcode to check if the UIButton is on top or not.
If you're using Autolayout to build your UI, make sure to declare all your constraints in your view and parent views.
Some times we forget to set all of them and the system complains about it, drawing your UI but not responding correctly in your touch events.
In my case, i’ve forgot to set my bottom constraint in my view so i’m here to set this comment as a reminder.
When a view like a button is placed next to another view like an image view etc. especially if if the image view is on top of another view Xcode sometimes thinks that your button is underneath that image view even though it is not. Sometimes you need to clear all the constraints and reset all constraints in order for the button to work.

Resources