I have a UISearchBar integrated in my navigation bar with this code:
// not needed because it's default
navigationItem.hidesSearchBarWhenScrolling = true
self.navigationItem.searchController = searchController
Also I add a custom bookmark icon like this:
searchController.searchBar.setImage(icon, for: .bookmark, state: .normal)
searchController.searchBar.showsBookmarkButton = true
searchController.searchBar.layoutIfNeeded()
This produces this weird look in iOS 11.4 and 12.1.4
It seems that the text field doesn't clip the icon and also doesn't apply the fade animation like for the placeholder and the search icon.
Do you guys see some error on my side?
If not, can someone reproduce this?
Then it is a bug and I will file a radar.
Okay I worked around the issue by doing this:
searchController.searchBar.allSubviews.forEach { $0.clipsToBounds = true }
Using this extension to get all nested subviews:
extension UIView {
var allSubviews: [UIView] {
return self.subviews.reduce([UIView]()) { $0 + [$1] + $1.allSubviews }
}
}
But this is a bit hacky so other solutions are appreciated :)
Try to do use clipToBounds instead of layoutIfNeeded()
searchController.searchBar.setImage(icon, for: .bookmark, state: .normal)
searchController.searchBar.showsBookmarkButton = true
searchController.searchBar.clipsToBounds = true
I have a custom UIButton but I am not able to make changing background or text color based on a quick tap. If something works, it's only on long press:
buton.isUserInteractionEnabled = true
buton.setTitle("text_normal", for: .normal)
buton.setTitle("text_highlighted", for: .highlighted)
buton.setTitle("text_selected", for: .selected)
buton.setTitle("text_focused", for: .focused)
The only text I can get is "text_highlighted" after holding the button ~1 second. But nothing happens on short tap.
But the action is triggered correctly:
let tap2 = UITapGestureRecognizer(target: self, action: #selector(handleTap))
buton.addGestureRecognizer(tap2)
#objc func handleTap(sender: UITapGestureRecognizer) {
print("click")
}
What I tried:
Adding custom
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
...
which is also triggered later. And combining with tocuhesEnded didn't change the color temporarily as well (maybe it was too quick).
I can't use UIButton(type: .system) as well - it's not system styled button.
Similar as with changing text this is not working as well:
self.setTitleColor(UIColor.gray, for: .normal)
self.setTitleColor(UIColor.yellow, for: .selected)
self.setTitleColor(UIColor.yellow, for: .highlighted)
self.setTitleColor(UIColor.yellow, for: .focused)
I can see yellow only when long pressed. I am looking for something like this, but it needs to work on quick tap as well.
Custom button is modifying layoutSubviews(), but not colors. Custom button contains default image and label. Whole button has rounded corners. But overall nothing special is in there.
I am not using any storyboard or XIB - everything is in Swift 4 programatically.
The button is supposed to lead to another ViewController, but I want to see the immediate feedback on click. Something like when created from storyboard: https://youtu.be/lksW12megQg?t=3m25s - not even simple alpha change works for me right now.
check isselected property of uibutton if button is selected then change the background color of button
Jen Jose was right - there was a problem with my parent class which was 'eating up' my touches. I confirmed this when moving it to different ViewController + I had the same issue with table, which couldn't handle touch events (https://stackoverflow.com/a/9248827/1317362)
EDIT:
To be precise - this button was in a UIScrollView.
Adding scrollView.delaysContentTouches = NO; solved the issue completely.
Details: https://stackoverflow.com/a/16650610/1317362
I have an app that have user feedback feature. I have done the view as shown in the image.
Feedback view that I made
Message area is using UITextView.
What I want to do now is when user finished editing the textView, then they click other places such as the star rating or any area besides textview, it will hide the cursor on the textview but it will keep the written text at the same time.
Can anyone help me with this? Thank you.
You can make the tint color transparent to hide the cursor.
textView.tintColor = UIColor.clearColor()
In Swift 3:
textView.tintColor = UIColor.clear
Update for Swift 3.x:
textField.tintColor = .clear
Try this:
1) Add a tap gesture recogniser to your view in an appropriate place (viewDidLoad for example)
let tapRecognizer = UITapGestureRecognizer(target: self, action: "hideKeyboard")
view.addGestureRecognizer(tapRecognizer)
2) Then you need to add hideKeyboard method. Example implementation
func hideKeyboard() {
view.endEditing(true)
}
That should do it :)
The alpha of a UIbutton goes to zero, and it becomes transparent when tapped. Ive added this IBAction
#IBAction func btnTapped(sender: UIButton) {
sender.highlighted = false
//...
}
and the UIButton still gets transparent when touched. How to prevent that?
I wrestled with this problem and finally fixed it by changing the button type from System to Custom.
Did you try something like:
yourButtonClass.setTitleColor(UIColor.blackColor(), forState: .Highlighted)
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.