Recently I've started to run into an issue with UIButton.Configuration API available starting from iOS 15 on a project targeting lower iOS versions. I have a UIButton added using Interface Builder, and for the sake of clearly showing this issue I gave it "This is button title set from Interface Builder" title. In my view controller I have the following code which updates title of the button:
class ViewController: UIViewController {
#IBOutlet private var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 15, *) {
var configuration = UIButton.Configuration.borderless()
configuration.title = "This is button title set programmatically"
button.configuration = configuration
} else {
button.setTitle("This is button title set programmatically", for: .normal)
}
}
}
It does update title of the button, but as soon as I touch it, title changes back to "This is button title set from Interface Builder". Furthermore, if I set button "Style" property in Interface Builder to "Default" rather than "Plain", it starts to completely ignore whatever value I set to configuration.title. Debugging shows that assigned button configuration does indeed have that updated title, but it is completely different from what it renders on the screen:
(lldb) po button.configuration?.title
▿ Optional<String>
- some : "This is button title set programmatically"
This what button title jumping back to the value from Interface Builder looks like:
And this is how the button is configured in Interface Builder:
The only fix I found so far is to update the title of this button inside of a configuration update handler, like this:
var configuration = UIButton.Configuration.borderless()
...
button.configuration = configuration
button.configurationUpdateHandler = { button in
button.configuration?.title = "This is button title set programmatically"
}
The question is do I really need to have a configuration update handler, if I only want my button to have the same title for different control states, or is it a known bug?
To me it seems really unnecessary to add it if I'm only going to have one title for other states as well.
The problem is that you have put your app into an inconsistent state. If this app is supposed to run on iOS 14, you cannot use the Plain button configuration in Interface Builder, as the iOS 14 device will have no idea what on earth to make of that and you'll crash.
What you need to do is set the interface builder button as a Default button and then, if you find you're running on iOS 15 or later, rip the button right out of the interface and replace it with a configuration-based button, along these lines:
var configuration = UIButton.Configuration.borderless()
configuration.title = "This is button title set programmatically"
let newButton = UIButton(configuration: configuration)
newButton.frame = button.frame
button.removeFromSuperview()
button = newButton
self.view.addSubview(button)
Either that or just give up on trying to use button configurations in a project intended to support iOS 14. Instead, just stick to old-fashioned setTitle(_:for:), etc.
Or else stop trying to support iOS 14 entirely.
In other words: Don't use button configurations at all until the only systems your app runs are systems that actually support button configurations.
Related
Currently I browse a session using the following:
let mcBrowser = MCBrowserViewController(serviceType: "service", session: p2pSession)
mcBrowser.delegate = self
mcBrowser.maximumNumberOfPeers = 1
listVC.presentAsModalWindow(mcBrowser)
Which produces a view:
I want to know if there's way to customize the appearance of the view controller before it's presented? For instance change the back ground of the list view to be dark instead of white. Or perhaps add an additional button to the window.
I encountered with an issue. I want to display 3 buttons, that are used for filtering my tableView. I found an appropriate images from “SF symbols.” Except one, which is the same I used for filtering(bigger to smaller) but is rotated on 180 degrees. I don’t know how to rotate bar button item, so I decided add custom button to bar button item.
There’s a problem occur – button is very small.
After adding button to bar button item:
I tried to use image configuration, but it’s not very good because it looks different and not with the same spacing.
After configuring button's image:
In the debug view hierarchy I found that symbol config is “Medium”, meanwhile other bar button items have “Body, Large”. But I haven’t found anything how to change it.
I have few questions:
Is there a way to flip bar button item without adding custom button in it?
If the way in the first question impossible, is this real to configure image “dynamically” the same way as other are displayed?
My code:
class viewController: UIViewController {
#IBOutlet var youngToOldfilterButton: UIBarButtonItem!
enter code here
override func viewDidLoad() {
let filterButton = UIButton(type: .custom)
var image = UIImage(systemName: "line.3.horizontal.decrease.circle", withConfiguration: UIImage.SymbolConfiguration(pointSize: 22))
filterButton.setImage(image, for: .normal)
filterButton.sizeToFit()
filterButton.addTarget(self, action: #selector(action), for: .touchUpInside)
//rotation transform
filterButton.transform = CGAffineTransform(rotationAngle: 180 * .pi/180)
youngToOldfilterButton.customView = filterButton
}
#objc func action {
...
}
}
symbol config is “Medium”, meanwhile other bar button items have “Body, Large”. But I haven’t found anything how to change it.
That way is to use a symbol configuration. Your problem is you are using the wrong configuration:
SymbolConfiguration(pointSize: 22))
Does that say Body, Large? No. You want this:
SymbolConfiguration(textStyle: .body, scale: .large)
https://developer.apple.com/documentation/uikit/uiimage/symbolconfiguration/3294246-init
However, the very best solution would likely be to design your own custom symbol image based directly on the "decreasing" image. This takes some time, but it isn't difficult, and you obviously care a lot about the exact thickness and position of the bars, so it might be worth it. Watch https://developer.apple.com/videos/play/wwdc2021/10250 for info.
Hello I am a very new and inexperienced developer and I am trying to border around a button. I'm using the Storyboard and when I used ViewController.swift, I can't make the button a weak var. It is only allowing me to edit the actions. Please help, thank you!
When I usually do this it allows me to choose if I insert "Action, Outlet or Outlet Collection."
Your screenshot shows "Mates Scene" but your class name is ViewController. It also looks like "Mates Scene" has an embed segue to a ViewController, so it looks like you might be trying to add an IBOutlet to a child view controller from one of the parent view controller's views (the "Home Button").
If so, you can't do that. You may have meant to add "Home Button" to the class of your "Mates" view controller instead.
In recent versions of iOS Buttons don't have any shape by default. They are displayed as clickable text.
Make sure you are trying to connect your outlet and action from IB to the right target view controller, as suggested by Tyler.
Once you've got the outlet and action links working you can add code to change the appearance.
If you want to make your button a rounded rectangle, say, you can do that with code that manipulates the buttons' layer settings (corner radius, borderWidth, borderColor, and backgroundColor are pretty common properties to edit.)
Here is code that turns a button into a rounded rectangle withe a 1-pixel blue outline and a yellow background color:
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
if let button = button {
button.layer.cornerRadius = 5
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.blue.cgColor
button.layer.backgroundColor = UIColor.yellow.cgColor
}
}
Edit:
That creates a button that looks like this:
(Note that if you use a border you may need to add some padding so that the border doesn't "crowd" the button title as in my example.)
Drag and release, then from the little menu that appears select outlet rather than action. Then, to set borders do something like:
yourButton.layer.borderWidth = 10
yourButton.layer.borderColor = UIColor.red.cgColor
I added a button in the main storyboard in Xcode. This button has an image as the background and its title "blueDoor" is shown on top of the background (see photo below).
There will be three buttons like this and they are linked to one IBAction. I would like to use sender.currentTitle to let the program know which button is clicked, but I don't want the text to show on the image.
How can I hide the text but still keep the title attribute so sender.currentTitle can be used? Or is there another way to do so?
A button with an image as the background:
You can use tag to do this.
Open storyboard > select your button > select Show the Attributes Inspector section in the right bar of the Xcode > scroll down and find Tag under the View section. And give different tags to your buttons.
Then your IBAction should be like this ->
#IBAction func didTapMyButtons(_ sender: UIButton) {
switch sender.tag:
case 0: // it is your first button's tag
// Do something with button which has tag `0`
case 1: // it is your second button's tag
// Do something with button which has tag `1`
case 2: // it is your third button's tag
// Do something with button which has tag `2`
default: break
}
Shortly: you can not do that. The currentTitle property is essentially a getter for button.titleLable.text. So is title(for: UIControl.State) setter (setTitle(String?, for: UIControl.State))
What docs say:
var currentTitle: String? { get }
Discussion
The value for this property is set automatically whenever
the button state changes. For states that do not have a custom title
string associated with them, this method returns the title that is
currently displayed, which is typically the one associated with the
normal state. The value may be nil.
This means whatever changes you bring to titleLabel.text, it is automatically reflected on a currentTitle property.
You can use tags as suggested or add a custom action handler for every button as you create them. E.g. for cancel and proceed buttons one will do it like so:
self.cancelButton.addTarget(self, action: #selector(self.cancelActionMethod), for: .touchUpInside)
self.proceedButton.addTarget(self, action: #selector(self.proceedActionMethod), for: .touchUpInside)
And then somewhere inside self:
#objc func cancelActionMethod() {
// some cancel specific actions
}
#objc func proceedActionMethod() {
// some cancel specific actions
}
I am trying to show my long text on Navigation Bar in 2 lines. I am using LargeTitles.
What I need is this https://i.stack.imgur.com/Yb85L.png // sorry cant post picture.
Image was taken from this post
I have tried a lot of examples and have achieved in showing the title in 2 lines. but the problem is that when I open a ViewController with a long title it jumps to adjust the title. Following is my code I have tried. I want to avoid jumping (increasing) and show the multiline text as same as it if using single line text. the normal effect.
func multiLineHeader() {
if #available(iOS 11.0, *) {
if let subviews = self.navigationController?.navigationBar.subviews {
for navItem in subviews {
for itemSubView in navItem.subviews {
if let largeLabel = itemSubView as? UILabel {
if largeLabel.canFitInSingleLine() { return }
largeLabel.numberOfLines = 2
largeLabel.lineBreakMode = .byWordWrapping
largeLabel.sizeToFit()
self.navigationController?.navigationBar.frame.size.height += largeLabel.frame.height
self.navigationController?.navigationBar.layoutIfNeeded()
}
}
}
}
}
}
I am calling this from viewDidAppear(). I tried calling from viewDidLoad() and viewWillAppear() does not work event title is not in 2 lines. as it was when called from viewDidAppear().
If there is anything which is not clear, comment and I will add more details.
This is not a solution to solve the multiline jump effect.
My question was based on the multiline title which I saw in the App Store(app) which did not have a jump effect. After a while when I dig deep into it. I found out that even in the App Store there is a jump effect on some screens and in some places App Store is using view rather than navigation bar.
For example, if you go to the App Store and search for Thunderbox entertainment.
1) Go to one of its apps and then click on company name to see all apps you will see a navigation bar with jump effect.
2) If you open the company profiles directly from the search screen you won't see any jump effect.