I am using the Picker in SwiftUI with the style of SegmentedPicker.
I am trying to change the colors of the picker and found this code in stackoverflow.
How to change selected segment color in SwiftUI Segmented Picker
init() {
UISegmentedControl.appearance().selectedSegmentTintColor? = .blue
UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.blue], for: .normal)
}
But an error pops up saying "Return from initializer without initializing all stored properties"
What can I do to make the error diappear :(
If you provide a own init() method, you have to initialize all your your properties. As almost every property got a default value, you should only have to initialize the Binding to make your code work...
init(showLoginView: Binding<Bool>) {
self._showLoginView = showLoginView //<< here init your stored properties
UISegmentedControl.appearance().selectedSegmentTintColor? = .blue
UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.blue], for: .normal)
}
If you're going to call this in the init within LoginSheetView, you also need to set up all of the other variables within the init function.
When you set the UISegmentedControl.appearance(), it is setting the appearance globally across you app for all UISegmentedControls. Since it therefore doesn't need to be called within LoginSheetView, the easy solution would be to move this init to one of your initial views that doesn't have other init variables, such as the ___App.swift file where you #main is called.
Related
Xcode 12.0.1
I am trying to remove the title label text on a UIButton using the storyboard. I then plan on setting it programmatically. When removing the text via the storyboard, the preview shows the text removed. However, the text remains when I build and run the application.
Changing programmatically is not working either. I try to change in in the controllers viewDidLoad()
punchBtn.setTitle("Transfer", for: .normal)
punchBtn.setTitle("Transfer", for: .application)
punchBtn.setTitle("Transfer", for: .selected)
punchBtn.setTitle("Transfer", for: .reserved)
punchBtn.setTitle("Transfer", for: .highlighted)
punchBtn.setTitle("Transfer", for: .disabled)
punchBtn.setTitle("Transfer", for: .focused)
The interesting thing is the text is changing when the button is selected:
Any advice would be greatly appreciated.
In the file Main.strings you will find this word "punch".
change it from there or else take a new button.
This does sound like a cache issue, because nowhere in the app does it say "Punch" anymore.
Command-Shift-K to clean the project hopefully fixes that.
If that doesn't work, then search the entire workspace for instances where you used the word "punch".
Try this!!!
override func viewWillAppear(_ animated: Bool) {
// other code
// end of method
// Change the text of your button
}
I am trying to make my button, when tapped, to push to a new View Controller. I've tried many different ways but it won't trigger the function that I have it linked to. I also checked the 3D stack view of my layers and the button is on top and clickable, even when I check the background color, it's not being covered by anything else.
Does anyone have any ideas to what I am doing wrong?
For now I am trying to make the button print out the sentence in the console, however whenever I press it, the string doesn't pop up, so I haven't bothered to connect it to the view controller yet.
Also, I am coding this app without storyboards.
Here is my code below.
It is under the MainPageCell class declared as a UICollectionViewCell
private let playButton: UIButton = {
let button = UIButton()
button.setTitle("", for: .normal)
button.backgroundColor = .clear
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(MainPageCell.buttonTapped), for: .touchUpInside)
return button
}()
#objc func buttonTapped() {
print("I PRESSED THE BUTTON")
}
This line is wrong:
button.addTarget(self, action: #selector(MainPageCell.buttonTapped), for: .touchUpInside)
You cannot assign self as the action target in a property declaration initializer, because the instance designated by self does not exist yet. There is no error or warning (I regard that as a bug), but the action method is never called.
Move that assignment elsewhere and rewrite it, like this:
self.playButton.addTarget(self, action: #selector(MainPageCell.buttonTapped), for: .touchUpInside)
Maybe try defining your button action under the UIView Class, I've had a problem like that before, only worked when i linked it to the View Class, Good luck
How can I change the text color of just one specific segment of a UISegmentedControl? I want to keep them all normal, except for a specific segment which should be a different color, whether it is selected or not.
#IBDesignable
class DesignableSegmentControl: UISegmentedControl{
}
extension UISegmentedControl{
#IBInspectable
var textColor: UIColor{
get {
return self.textColor
}
set {
let unselectedAttributes = [NSAttributedString.Key.foregroundColor: newValue,
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13, weight: UIFont.Weight.regular)]
self.setTitleTextAttributes(unselectedAttributes, for: .normal)
self.setTitleTextAttributes(unselectedAttributes, for: .selected)
}
}
}
Just change you segment control's class name as shown below:
[change class name]:
[change text color]:
for swift 5:
yourSegment.setTitleTextAttributes( [NSAttributedString.Key.foregroundColor: UIColor.white], for: .selected)
Updated Swift 4.1
UISegmentedControl.appearance().setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor.red], for: .selected)
Swift 3.1
UISegmentedControl.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.red as Any], for: .selected)
For Earlier Swift Version:
UISegmentedControl.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.red], for: .selected)
There isn't any built-in way; as you've probably figured out, setTitleTextAttributes applies to all the segments. You would have to draw your text as an image and use that instead.
Just add this line of code:
UISegmentedControl.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.red], for: .selected)
You can implement your own segmented control for customisation.
Add two buttons with same UI as segmented control.
Round the corners.
On click make the background colour according to your need and clear back ground of other and make it unselected.
In this way there is large scope for customisation.
Also there are many custom segmented control are available at GitHub.
You can try this
https://github.com/gmarm/BetterSegmentedControl
Within my app i'm trying to back a UIButton, i just want it to print "this is a test" When i press the button it does the classic button animation, however there is nothing printed in the console.
var tbutton: UIButton = {
let button = UIButton(type: .system)
button.frame = CGRect(x: 40.0, y:400.0, width: 300.0, height: 300.0)
let image = UIImage(named: "backb")
button.setBackgroundImage(image, for: .normal)
button.addTarget(self, action: #selector(dothings), for: .touchUpInside)
return button
}()
#objc func dothings(){
print("this is a test")
}
I then add the button into view with:
view.addSubview(tbutton)
Is there a section of code i'm missing, or have i coded something wrong?
You shouldn't initialize your button in that way.
Quoting the Apple documentation from Setting a Default Property Value with a Closure or Function:
If you use a closure to initialize a property, remember that the rest of the instance has not yet been initialized at the point that the closure is executed. This means that you cannot access any other property values from within your closure, even if those properties have default values. moreover:
You also cannot use the implicit self property, or call any of the instance’s methods, hence the problem is here:
button.addTarget(self, action: #selector(dothings), for: .touchUpInside)
so to fix the issue you should move the button initialization (or move the addTarget) after your ViewController is fully initialized (eg: viewDidLoad).
Another way to fix the issue, assuming you are using such button only after viewDidLoad, is to define it as a lazy var:
A lazy stored property is a property whose initial value is not calculated until the first time it is used
Why in earlier when we invoke self in a computed property like this example we would need to write lazy var but now we don't have to. why?
let(lazy var in earlier times) pauseButton: UIButton = {
let button = UIButton(type: .system)
let image = UIImage(named: "pause")
button.setImage(image, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.tintColor = .white
button.addTarget(self, action: #selector(handlePause), for: .touchUpInside)
return button
}()
I think there is a misunderstanding, which is what you mentioned in the code snippet is not a computed property! it is just a stored property which has been initialized by a closure; As mentioned in the Swift Initialization -
Setting a Default Property Value with a Closure or Function:
If a stored property’s default value requires some customization or
setup, you can use a closure or global function to provide a
customized default value for that property. Whenever a new instance of
the type that the property belongs to is initialized, the closure or
function is called, and its return value is assigned as the property’s
default value.
You could check: Difference between computed property and property set with closure.
Note that the closure of pauseButton will be executed without even using it, if you tried to check it (add a breakpoint in it), you will notice that. I assume this is not what are your expecting -and not what are you aiming to-, so you should declare it as lazy var instead of let.
However,
Referring to the same Swift documentation:
If you use a closure to initialize a property, remember that the rest
of the instance has not yet been initialized at the point that the
closure is executed. This means that you cannot access any other
property values from within your closure, even if those properties
have default values. You also cannot use the implicit self property,
or call any of the instance’s methods.
Implying that:
class MyViewController: UIViewController {
let btnTitle = "pause"
let pauseButton: UIButton = {
let button = UIButton(type: .system)
let image = UIImage(named: btnTitle)
button.setImage(image, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.tintColor = .white
button.addTarget(self, action: #selector(handlePause), for: .touchUpInside)
return button
}()
func handlePause() { }
}
Will gives an error on the let image = UIImage(named: btnTitle):
That should also be applicable for any other instance member, for instance, if you would try to add view.addSubview(button) into the closure, you will get the same error for view instance member.
But for a reason (I have no idea why), working with selectors seems to be a special case, because button.addTarget(self, action: #selector(handlePause), for: .touchUpInside) worked fine for me (Xcode 9.0), nevertheless if you tried to add self to it, as:
button.addTarget(self, action: #selector(self.handlePause), for: .touchUpInside)
you would get the following error:
If you use button.addTarget in a regular stored property, you won't get a compile-time error. But I'm pretty sure it is a bug. I experimentally realize that selectors in regular stored properties causes unpredictable results in iOS versions 14.2 and higher. The selector may be released or any other selector that is given in the stored property closure may be associated with the button. As result, tapping on a button may trigger an action that is intended to be triggered by another button.
To do not tussle with such issues, I stick to the old way and use button.addTarget only in lazy stored properties.