Is there a way to change the button titles on the SLComposeServiceViewController? I tried to change the bar button items on the navigation item, but those aren't the right buttons.
Simply accessing from navigationController!.navigationBar does the charm. The following should help.
self.navigationController!.navigationBar.topItem!.rightBarButtonItem!.title = "Save"
I just found a way to do it:
class CustomServiceViewController: SLComposeServiceViewController {
override func viewDidLoad() {
let navigationBar = view.subviews.first?.subviews?.last? as? UINavigationBar
let postButton = navigationBar?.subviews.last? as? UIButton
let cancelButton = navigationBar?.subviews.last? as? UIButton
postButton?.setTitle("Done", forState: .Normal)
}
}
Be warned - it's a fragile solution, based on undocumented internals of SLComposeServiceViewController
The answer by Kasztan no longer works with the latest iOS; here is the latest fragile solution..
class CustomServiceViewController: SLComposeServiceViewController {
override func viewDidLoad() {
let navigationBar = view.subviews.last?.subviews?.last? as? UINavigationBar
let postButton = navigationBar?.subviews[3] as? UIButton
postButton?.setTitle("Done", forState: .Normal)
}
}
EDIT #3: Solution working on iOS 9 and iOS 10 beta
The previous approach stopped working with iOS 9, but the following seems to work again (tested on iOS 9 and 10 beta 2):
1) First, you need to add a UIFont class extension to check if a button font is bold (this, because the Post button is always bold); here's how.
2) Then, in viewDidAppear:, we need the following code (an updated version of the code I wrote in Edit 2):
if let navigationBar = self.navigationController?.navigationBar {
// First, let's set backgroundColor and tintColor for our share extension bar buttons
navigationBar.backgroundColor = UIColor.darkGrayColor()
navigationBar.tintColor = UIColor.whiteColor()
if let navBarSubviews = navigationBar.subviews as? [UIView] {
for eachView in navBarSubviews {
if let navBarButton = eachView as? UIButton {
// Second, let's set our custom titles for both buttons (Cancel and Post); checking for the title wouldn't work for localized devices, so we check if the button is bold (Post) or not (Cancel) via the UIFont class extension above.
let buttonFont : UIFont? = navBarButton.titleLabel?.font
if buttonFont?.isBold == true {
navBarButton.setTitle("Save", forState: .Normal)
} else {
navBarButton.setTitle("Cancel", forState: .Normal)
}
}
}
}
}
Of course, this works now, but it will probably break again in the future...
EDIT #2: I made it work on a device with iOS 8.4 :)
Turns out I was wrong, after spending an unreasonable amount of time on this I've been able to both change the color of the buttons and their text.
Here's my code, that needs to be put inside ViedDidAppear() (if you place it in viewDidLoad() it won't work!):
if let navigationBar = self.navigationController?.navigationBar {
// First, let's set backgroundColor and tintColor for our share extension bar buttons
navigationBar.backgroundColor = UIColor.darkGrayColor()
navigationBar.tintColor = UIColor.whiteColor()
if let navBarSubviews = navigationBar.subviews as? [UIView] {
for eachView in navBarSubviews {
if let navBarButton = eachView as? UIButton {
// Second, let's set our custom titles for both buttons (Cancel and Post); checking for the title wouldn't work on localized devices, so we check if the current button is emphasized (Post) or not (Cancel) via an UIFontDescriptor.
let fontDescriptor : UIFontDescriptor? = navBarButton.titleLabel?.font.fontDescriptor()
if let descriptor = fontDescriptor {
let fontAttributes : NSDictionary = descriptor.fontAttributes()
var buttonFontIsEmphasized : Bool? = fontAttributes["NSCTFontUIUsageAttribute"]?.isEqualToString("CTFontEmphasizedUsage")
if buttonFontIsEmphasized == true {
navBarButton.setTitle("Save", forState: .Normal)
} else {
navBarButton.setTitle("Cancel", forState: .Normal)
}
}
}
}
}
}
Still, I'm not sure this should be done on a shipping app nor it would pass App Review (it should, though, because it doesn't mess with private APIs).
Also, it should be noted that this could break anytime, even though it shouldn't be as easily breakable as the previous solutions (it iterates through the subviews and attempts downcasting them, so a small change in the view hierarchy shouldn't render it useless); my expectations is that, even if in the future it stops working, it shouldn't crash the Share Extension.
Original answer
I believe what you (and I) want to do is not possible anymore, possibly by design. Here's why:
Inspired by #Kasztan and #Paito answers, I tried this in viewDidLoad() of my ShareViewController:
for eachView in view.subviews {
println("1")
for eachSubView in eachView.subviews {
println("2")
if let navigationBarView = eachSubView as? UINavigationBar {
println("3")
for eachNavBarSubView in navigationBarView.subviews {
println("4")
if let navBarButton = eachNavBarSubView as? UIButton {
println("5")
println(navBarButton.titleForState(.Normal))
navBarButton.setTitleColor(UIColor.redColor(), forState: .Normal)
navBarButton.setTitle("My text", forState: .Normal)
navBarButton.tintColor = UIColor.redColor()
navBarButton.setNeedsLayout()
navBarButton.layoutIfNeeded()
}
}
}
}
}
Not that I believe something like this should ship in an app, but as a proof of concept this should have worked and, in theory, should be a bit less breakable with future releases of the OS.
Except, it didn't work for me on iOS 8.4: I see all the checkpoint messages logged, from 1 to 5, some of them multiple times (as it should be, since the code tries every possible subview).
The "5" message is logged twice, which makes sense since it means that it successfully downcast both the buttons, Cancel and Post, but not the text nor the color is changed from the default.
My conclusion is that something in Apple's code prevents us to change the appearance of those buttons.
Of course, if anyone finds a solution, I'd be glad to downvote my own answer (if it can be done, I'm note sure) ;)
EDIT #1: One last check, I logged the button title too, after the buttons downcast (5), and yes, I got Optional("Cancel") and Optional("Post") in the console, so this solution gets the right buttons, but they can't be edited.
Related
I have a button that contains a finger print image like the picture above. that fingerPrint button has a function to segue to viewController2. that fingerPrint button exists on the viewController1.
when the viewController1 is opened for the first time, I will get a data from the server, if I don't have that data, I shouldn't segue to viewController2 .
but sometimes we have an error as the response of my request. if I get the error response, then I want that finger button to be rectangular button that has 'Refresh' as the title and has different function to make request again to the server
how to achieve change the shape of existing UI Button?
class ViewController1 : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
getDataFromServer()
}
#IBAction func fingerPrintButtonDidPressed(_ sender: Any) {
}
func getDataFromServer() {
// send request to get data from server
}
}
It is usually best to do it either all in code or all in storyboard. To do it in storyboard I suggest one of the following:
Create 2 buttons and in code hide one or another depending on your state
Assign different images and texts for different button states (there is a dropdown in interface builder) and then change the state of a button in runtime
To do it in code overall I suggest simply changing the values.
Assume you have something like this:
enum ViewState {
case idle
case success
case failed
}
var state: ViewState = .idle {
didSet {
refreshButton()
}
}
Now you can set your state when you get a response or on any event needed. All you need to implement is refreshButton.
func refreshButton() {
switch state {
case .idle:
button.setTitle(nil, for: .normal)
button.setImage(fingerprintImage, for: .normal)
button.backgroundColor = .clear
case .success:
button.setTitle(nil, for: .normal)
button.setImage(fingerprintImage, for: .normal)
button.backgroundColor = .clear
case .failed:
button.setTitle("REFRESH", for: .normal)
button.setImage(nil, for: .normal)
button.backgroundColor = .green
}
}
You can then use the same switch inside your button action (on press).
if isError
{
button.type = .system
button.backgroundColor - UIColor.green
button.setTitle("REFRESH" for: .normal)
button.addTarget(--------)
}
else
{
button.type = .custom
button.setImage(UIImage(named: "yourImageName.png") for: .normal)
button.addTarget(--------)
}
Simple Solution is : Use two buttons one for figure Image second for Refresh text. And use button isHidden property vice-versa.
For Auto layout
If you are using Auto layout then use constraint IBOutlet and change it's constant property value. it will change your button height or width.
For Auto resize
if you are using Auto resize than you can change you button frame which you want
I have been searching and trying for days but since it's a new thing there's not much information available (or I can't find them).
Anyway, I am trying to get use of new NavBar features in iOS 11 but I want to put both a logo and a large title (of each ViewController) in my NavBar. Something like the screenshot below:
How is it possible to implement this?
Side Hint:
For same app in iOS 10 I set the logo to show instead of titleView in NavBar but now in newer version we want to add the Title too.
Thanks in advance
Without your code sample it's hard to say what goes wrong for you, but this just works:
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 11, *) {
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .always
title = "Your title"
}
let titleImageView = NavigationImageView()
titleImageView.image = UIImage(named: "yourImageName")
navigationItem.titleView = titleImageView
}
EDIT: To set custom size for your title view you can subclass your UIView (UIImageView) and override sizeThatFits
class NavigationImageView: UIImageView {
override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: 50, height: 50)
}
}
Did you try setting a background image for your navbar? Like this:
// Set Navigation bar background Image
let navBgImage:UIImage = UIImage(named: "[image.jpg]")!
self.navigationController!.navigationBar.setBackgroundImage(navBgImage, for: .default)
Goal
I want to make a custom keyboard that is only used within my app, not a system keyboard that needs to be installed.
What I have read and tried
Documentation
App Extension Programming Guide: Custom Keyboard
Custom Views for Data Input
The first article above states:
Make sure a custom, systemwide keyboard is indeed what you want to
develop. To provide a fully custom keyboard for just your app or to
supplement the system keyboard with custom keys in just your app, the
iOS SDK provides other, better options. Read about custom input views
and input accessory views in Custom Views for Data Input in Text
Programming Guide for iOS.
That is what led me to the second article above. However, that article did not have enough detail to get me started.
Tutorials
iOS 8: Creating a Custom Keyboard in Swift
How to make a custom keyboard in iOS 8 using Swift
Xcode 6 Tutorial: iOS 8.0 Simple Custom Keyboard in Swift
Creating a Custom Keyboard Using iOS 8 App Extension
I was able to get a working keyboard from the second tutorial in the list above. However, I couldn't find any tutorials that showed how to make an in app only keyboard as described in the Custom Views for Data Input documentation.
Stack Overflow
I also asked (and answered) these questions on my way to answering the current question.
How to input text using the buttons of an in-app custom keyboard
Delegates in Swift
Question
Does anyone have a minimal example (with even one button) of an in app custom keyboard? I am not looking for a whole tutorial, just a proof of concept that I can expand on myself.
This is a basic in-app keyboard. The same method could be used to make just about any keyboard layout. Here are the main things that need to be done:
Create the keyboard layout in an .xib file, whose owner is a .swift file that contains a UIView subclass.
Tell the UITextField to use the custom keyboard.
Use a delegate to communicate between the keyboard and the main view controller.
Create the .xib keyboard layout file
In Xcode go to File > New > File... > iOS > User Interface > View to create the .xib file.
I called mine Keyboard.xib
Add the buttons that you need.
Use auto layout constraints so that no matter what size the keyboard is, the buttons will resize accordingly.
Set the File's Owner (not the root view) to be the Keyboard.swift file. This is a common source of error. See the note at the end.
Create the .swift UIView subclass keyboard file
In Xcode go to File > New > File... > iOS > Source > Cocoa Touch Class to create the .swift file.
I called mine Keyboard.swift
Add the following code:
import UIKit
// The view controller will adopt this protocol (delegate)
// and thus must contain the keyWasTapped method
protocol KeyboardDelegate: class {
func keyWasTapped(character: String)
}
class Keyboard: UIView {
// This variable will be set as the view controller so that
// the keyboard can send messages to the view controller.
weak var delegate: KeyboardDelegate?
// MARK:- keyboard initialization
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeSubviews()
}
override init(frame: CGRect) {
super.init(frame: frame)
initializeSubviews()
}
func initializeSubviews() {
let xibFileName = "Keyboard" // xib extention not included
let view = Bundle.main.loadNibNamed(xibFileName, owner: self, options: nil)![0] as! UIView
self.addSubview(view)
view.frame = self.bounds
}
// MARK:- Button actions from .xib file
#IBAction func keyTapped(sender: UIButton) {
// When a button is tapped, send that information to the
// delegate (ie, the view controller)
self.delegate?.keyWasTapped(character: sender.titleLabel!.text!) // could alternatively send a tag value
}
}
Control drag from the buttons in the .xib file to the #IBAction method in the .swift file to hook them all up.
Note that the protocol and delegate code. See this answer for a simple explanation about how delegates work.
Set up the View Controller
Add a UITextField to your main storyboard and connect it to your view controller with an IBOutlet. Call it textField.
Use the following code for the View Controller:
import UIKit
class ViewController: UIViewController, KeyboardDelegate {
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// initialize custom keyboard
let keyboardView = Keyboard(frame: CGRect(x: 0, y: 0, width: 0, height: 300))
keyboardView.delegate = self // the view controller will be notified by the keyboard whenever a key is tapped
// replace system keyboard with custom keyboard
textField.inputView = keyboardView
}
// required method for keyboard delegate protocol
func keyWasTapped(character: String) {
textField.insertText(character)
}
}
Note that the view controller adopts the KeyboardDelegate protocol that we defined above.
Common error
If you are getting an EXC_BAD_ACCESS error, it is probably because you set the view's custom class as Keyboard.swift rather than do this for the nib File's Owner.
Select Keyboard.nib and then choose File's Owner.
Make sure that the custom class for the root view is blank.
The key is to use the existing UIKeyInput protocol, to which UITextField already conforms. Then your keyboard view need only to send insertText() and deleteBackward() to the control.
The following example creates a custom numeric keyboard:
class DigitButton: UIButton {
var digit: Int = 0
}
class NumericKeyboard: UIView {
weak var target: (UIKeyInput & UITextInput)?
var useDecimalSeparator: Bool
var numericButtons: [DigitButton] = (0...9).map {
let button = DigitButton(type: .system)
button.digit = $0
button.setTitle("\($0)", for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.addTarget(self, action: #selector(didTapDigitButton(_:)), for: .touchUpInside)
return button
}
var deleteButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("⌫", for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = "Delete"
button.addTarget(self, action: #selector(didTapDeleteButton(_:)), for: .touchUpInside)
return button
}()
lazy var decimalButton: UIButton = {
let button = UIButton(type: .system)
let decimalSeparator = Locale.current.decimalSeparator ?? "."
button.setTitle(decimalSeparator, for: .normal)
button.titleLabel?.font = .preferredFont(forTextStyle: .largeTitle)
button.setTitleColor(.black, for: .normal)
button.layer.borderWidth = 0.5
button.layer.borderColor = UIColor.darkGray.cgColor
button.accessibilityTraits = [.keyboardKey]
button.accessibilityLabel = decimalSeparator
button.addTarget(self, action: #selector(didTapDecimalButton(_:)), for: .touchUpInside)
return button
}()
init(target: UIKeyInput & UITextInput, useDecimalSeparator: Bool = false) {
self.target = target
self.useDecimalSeparator = useDecimalSeparator
super.init(frame: .zero)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - Actions
extension NumericKeyboard {
#objc func didTapDigitButton(_ sender: DigitButton) {
insertText("\(sender.digit)")
}
#objc func didTapDecimalButton(_ sender: DigitButton) {
insertText(Locale.current.decimalSeparator ?? ".")
}
#objc func didTapDeleteButton(_ sender: DigitButton) {
target?.deleteBackward()
}
}
// MARK: - Private initial configuration methods
private extension NumericKeyboard {
func configure() {
autoresizingMask = [.flexibleWidth, .flexibleHeight]
addButtons()
}
func addButtons() {
let stackView = createStackView(axis: .vertical)
stackView.frame = bounds
stackView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(stackView)
for row in 0 ..< 3 {
let subStackView = createStackView(axis: .horizontal)
stackView.addArrangedSubview(subStackView)
for column in 0 ..< 3 {
subStackView.addArrangedSubview(numericButtons[row * 3 + column + 1])
}
}
let subStackView = createStackView(axis: .horizontal)
stackView.addArrangedSubview(subStackView)
if useDecimalSeparator {
subStackView.addArrangedSubview(decimalButton)
} else {
let blank = UIView()
blank.layer.borderWidth = 0.5
blank.layer.borderColor = UIColor.darkGray.cgColor
subStackView.addArrangedSubview(blank)
}
subStackView.addArrangedSubview(numericButtons[0])
subStackView.addArrangedSubview(deleteButton)
}
func createStackView(axis: NSLayoutConstraint.Axis) -> UIStackView {
let stackView = UIStackView()
stackView.axis = axis
stackView.alignment = .fill
stackView.distribution = .fillEqually
return stackView
}
func insertText(_ string: String) {
guard let range = target?.selectedRange else { return }
if let textField = target as? UITextField, textField.delegate?.textField?(textField, shouldChangeCharactersIn: range, replacementString: string) == false {
return
}
if let textView = target as? UITextView, textView.delegate?.textView?(textView, shouldChangeTextIn: range, replacementText: string) == false {
return
}
target?.insertText(string)
}
}
// MARK: - UITextInput extension
extension UITextInput {
var selectedRange: NSRange? {
guard let textRange = selectedTextRange else { return nil }
let location = offset(from: beginningOfDocument, to: textRange.start)
let length = offset(from: textRange.start, to: textRange.end)
return NSRange(location: location, length: length)
}
}
Then you can:
textField.inputView = NumericKeyboard(target: textField)
That yields:
Or, if you want a decimal separator, too, you can:
textField.inputView = NumericKeyboard(target: textField, useDecimalSeparator: true)
The above is fairly primitive, but it illustrates the idea: Make you own input view and use the UIKeyInput protocol to communicate keyboard input to the control.
Also please note the use of accessibilityTraits to get the correct “Spoken Content” » “Speak Screen” behavior. And if you use images for your buttons, make sure to set accessibilityLabel, too.
Building on Suragch's answer, I needed a done and backspace button and if you're a noob like me heres some errors you might encounter and the way I solved them.
Getting EXC_BAD_ACCESS errors?
I included:
#objc(classname)
class classname: UIView{
}
fixed my issue however Suragch's updated answer seems to solve this the more appropriate/correct way.
Getting SIGABRT Error?
Another silly thing was dragging the connections the wrong way, causing SIGABRT error. Do not drag from the function to the button but instead the button to the function.
Adding a Done Button
I added this to the protocol in keyboard.swift:
protocol KeyboardDelegate: class {
func keyWasTapped(character: String)
func keyDone()
}
Then connected a new IBAction from my done button to keyboard.swift like so:
#IBAction func Done(sender: UIButton) {
self.delegate?.keyDone()
}
and then jumped back to my viewController.swift where i am using this keyboard and added this following after the function keyWasTapped:
func keyDone() {
view.endEditing(true)
}
Adding Backspace
This tripped me up a lot, because you must set the textField.delegate to self in the viewDidLoad() method (shown later).
First: In keyboard.swift add to the protocol func backspace():
protocol KeyboardDelegate: class {
func keyWasTapped(character: String)
func keyDone()
func backspace()
}
Second: Connect a new IBAction similar to the Done action:
#IBAction func backspace(sender: UIButton) {
self.delegate?.backspace()
}
Third: Over to the viewController.swift where the NumberPad is appearing.
Important: In viewDidLoad() set all textFields that will be using this keyboard. So your viewDidLoad() should look something like this:
override func viewDidLoad() {
super.viewDidLoad()
self.myTextField1.delegate = self
self.myTextField2.delegate = self
// initialize custom keyboard
let keyboardView = keyboard(frame: CGRect(x: 0, y: 0, width: 0, height: 240))
keyboardView.delegate = self // the view controller will be notified by the keyboard whenever a key is tapped
// replace system keyboard with custom keyboard
myTextField1.inputView = keyboardView
myTextField2.inputView = keyboardView
}
I'm not sure how to, if there is a way to just do this to all textFields that are in the view. This would be handy...
Forth: Still in viewController.swift we need to add a variable and two functions. It will look like this:
var activeTextField = UITextField()
func textFieldDidBeginEditing(textField: UITextField) {
print("Setting Active Textfield")
self.activeTextField = textField
print("Active textField Set!")
}
func backspace() {
print("backspaced!")
activeTextField.deleteBackward()
}
Explanation of whats happening here:
You make a variable that will hold a textField.
When the "textFieldDidBeginEditing" is called it sets the variable so it knows which textField we are dealing with. I've added a lot of prints() so we know everything is being executed.
Our backspace function then checks the textField we are dealing with and uses .deleteBackward(). This removes the immediate character before the cursor.
And you should be in business.
Many thanks to Suragchs for helping me get this happening.
I'm creating pages dynamically, each page contains a Navigation Controller and UIViewController.
Inside each page, there are components like link, images, texts.
Each component is a class like the following:
class link: Component, ComponentProtocol {
var text: String
var url: String
func browseURL(sender: UIButton!){
let targetURL = NSURL(fileURLWithPath: self.url)
let application = UIApplication.sharedApplication()
application.openURL(targetURL!)
}
func generateView() -> UIView?{
var result: UIView?
var y = CGRectGetMinY(frame)
var linkBtn = UIButton(frame: CGRect(x: 0, y:30 , width:300 , height: 50)
linkBtn.setTitle(self.text, forState: UIControlState.Normal)
linkBtn.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
linkBtn.titleLabel?.font = linkBtn.titleLabel?.font.fontWithSize(15)
// this doesn't seem to be registered properly
linkBtn.addTarget(self, action: "browseURL:", forControlEvents: UIControlEvents.TouchUpInside)
result = UIView()
result?.addSubview(linkBtn)
return result
}
Then in the ViewDidLoad of the page ViewController method I would have this to initialise the page components:
for component in components!{
if let acomponent:ComponentProtocol = component as? ComponentProtocol {
if let res = acomponent.generateView(innerFrame) {
if let view = res.view {
self.view.addSubview(view)
}
}
}
}
The button is showing, but when I touch nothing happens. When I debugged, the browseURL is no triggered at all.
What's wrong with my code? I'm guessing because I registered the action in link class and not in the ViewController of the page?
UPDATE
This could be a similar issue, but the answer is not so straightforward and I actually have the component reference in my ViewController: (Target: Object) not working for setting UIButton from outside viewController
I'm not at a Mac right now so I can't test, but does this work:
linkBtn.target = self
linkBtn.action = "browseURL:"
I think if you have selectors in Swift you have to be careful with colons and specifying #objc if your class doesn't inherit from NSObject, it could also be something to do with that?
I want to remove the clear button (gray x) from the UISearchBar. I tried to do it like described in this answer, but it doesn't work.
I translated the Objective-C code from the answer and the comment below to following Swift code:
for subview in searchBar.subviews {
configureSearchBarView(subview as UIView)
}
func configureSearchBarView(view: UIView) {
for subview in view.subviews {
self.configureSearchBarView(subview as UIView)
}
if subview.conformsToProtocol(UITextInputTraits) {
var clearView = view as UITextField
clearView.clearButtonMode = UITextFieldViewMode.Never
}
}
Unfortunatly this doesn't remove the clear button.
Another answer suggests to work with appearanceWhenContainedIn, but this method doesn't seem to be implemented in Swift.
Any ideas?
Swift
I found a better way that completely removes the button, using clearButtonMode = .never
let searchBarStyle = searchBar.value(forKey: "searchField") as? UITextField
searchBarStyle?.clearButtonMode = .never
Swift 5
Tested on iOS 13
As I pointed out in another answer, this is the one liner working for me to make it disappear completely:
searchBar.searchTextField.clearButtonMode = .never
However you may also set it to .whileEditing if you only want it displayed when the user is typing and then make it disappear when the search bar loses focus.
I found a solution. It is possible to exchange the clear button with a custom image:
UISearchBar.appearance().setImage(UIImage(named: "emptyImg"), forSearchBarIcon: UISearchBarIcon.Clear, state: UIControlState.Normal)
UISearchBar.appearance().setImage(UIImage(named: "emptyImg"), forSearchBarIcon: UISearchBarIcon.Clear, state: UIControlState.Highlighted)
emptyImg is an png that contains one white pixel. Found in this answer