ios14 - Button Title and Image Not getting Displayed - ios

Button title and image not geting rendered. Works fine in iOS13 and below, but after updating to Xcode12.0.1 and iOS14, it is not working.
let viewAllButton: RoundedButton = {
let button = RoundedButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .red
button.setTitle("View all", for: .normal)
button.setTitleColor(.white, for: .normal)
return button
}()
class RoundedButton: UIButton {
override func draw(_ rect: CGRect) {
super.draw(rect)
self.layer.cornerRadius = self.bounds.height / 2
self.layer.masksToBounds = true
}
}

Try to set the title color before setting setTitle. (I had some issues with something like this too).

Xcode 12.0.1, iOS 14, Swift 5.3
Actually the problem was in the redundant code which was lying around in my codebase.
extension UIButton {
open override func layoutSubviews() {
super.layoutSubviews()
}
}
I just had this sitting in my extensions. This didnt cause any issues in iOS 13 and below.
Steps to reproduce:
Add above code in your project.
Run the project(Release or Debug Mode) the project in your simulator(Button renders properly) and Kill the app.
Now, Launch the app by clicking App icon and you will see Button contents disappear.
Strange bug, i hope it save someones time.

Related

UISearchBar bookmark icon is not hiding on scroll

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

Set DLRadioButton marginWidth between icon and title

Before I upgraded to Swift 4.2 - Xcode 10.1 the DLRadioButton I used had an even spacing between the icon and the title. I never set the spacing and everything worked fine. After the upgrade the icon and the title overlaps
The cocoapod for it says that it uses a default marginWidth of kdefaultmarginwidth
I tried to set the marginWidth in code to anything that would definitely add spacing like 50.0 but the overlap stays. I read somewhere that the kdefaultmarginwidth spacing is 5.0
How can I fix the spacing?
code:
let saleButton: DLRadioButton = {
let button = DLRadioButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Sale", for: .normal)
button.setTitleColor(UIColor.lightGray, for: .normal)
button.marginWidth = 50.0 // I tried 5.0, 10.0, 20.0, even 100.0 but nothing
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(saleButton)
// constraints get set
}
This is a choppy fix but it works for now because everything else I tried didn't work. Inside the saleButton closure, I had to add 4 empty spaces before I set the string for the title:
I changed this:
button.setTitle("Sale", for: .normal)
to this and the overlapping is now gone
// there are 4 empty spaces in front of the word Sale
button.setTitle(" Sale", for: .normal)
Here is the image below:

Adding forwardIcon into a programmatically created UIButton

I have a UIButton created programmatically and I need to add a forward icon which has to be created using UIBezierPath as shown in the figure below. How do I do that . I am a beginner in iOS and I need help.
The end result I want is this
The code I have is this:
let forwardButton : UIButton = {
let fb = UIButton()
fb.translatesAutoresizingMaskIntoConstraints = false
fb.backgroundColor = UIColor(red:0.56, green:0.07, blue:1.00, alpha:1.0)
return fb
}()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
forwardButton.bounds.size.width
forwardButton.layer.masksToBounds = true
}
I have code for circular button but I don't know how to draw forward icon using bezierpath inside the circular button. any help would be appreciated
You can add this code :
fb.setImage(UIImage(named: "yourImageName.png"), for: .normal)
If you are using xcasset you can use (no need .png):
if let image = UIImage(named: "Done") {
fb.setImage(image, for: .normal)
}

iOS 11 Logo and title in NavigationBar

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)

Change the button titles on SLComposeServiceViewController?

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.

Resources