I'm using Xcode 10.1 where I tried to calculate the radius in a completion block by setting the value of another variable called as diameter. The code is given below
import UIKit
import Foundation
var radius:Double = 10
var diameter: Double{
get{
return radius * 2
}
set{
radius = newValue
}
}
diameter = 30
print(diameter)
I'm expecting that the output should be 60.0 but it showing:
error: Computed property.playground:43:1: error: variables currently
must have an initial value when entered at the top level of the REPL
var area: Double{
The output is properly shown in the online compiler of swift but not in Xcode. Now How to get the proper output?
You have to write this code in a class or struct then create the object of the class or struct then you have to assign diameter value and print it.
/**
This code you can run in playground
*/
class Demo {
var radius:Double = 10
var diameter: Double {
get{
return radius * 2
}
set{
radius = newValue
}
}
}
let d = Demo()
d.diameter = 30
print(d.diameter)
//Output
60.0
/**
This code you can run in Xcode.
*/
class ViewController: UIViewController {
var radius:Double = 10
var diameter: Double {
get{
return radius * 2
}
set{
radius = newValue
}
}
override func viewDidLoad() {
super.viewDidLoad()
diameter = 30
print(diameter) // Output: 60.0
}
}
Related
Is there a way for swift code to check if this variable changes and than change the shape of an object?
var Shape = 0.4
let a = (self.size.width + self.size.height) * Shape
Using property observers you can achieve what you are looking for. didSet will be called after varibale value changed.
var variable1 = 0 {
didSet {
onValueChange()
}
}
func onValueChange() {
print("Change \(variable1)")
}
I'm a newbie in swift and I'm trying to pass an instance of a class (CP) I made as a property of the ContentView struct for swift UI. This instance takes in parameter another property of the same ContentView struct. I want to be able to select an option from the alert menu, set the property to a variable that will determinate specific time sequences from a chronometer, and since the parameter of this instance is an inout string, the values will update in the instance. I found somebody in a similar situation on this website, and somebody suggested overriding the viewDidLoad function. However this does not apply to the SwiftUI framework.
Here is a sample of the code I believe is enough to understand the issue. débatCP, will be used in other elements so declaring it inside the first button action will not work.
struct ContentView: View {
#State var a = "Chronometre"
#State var partie = "Partie du débat"
#State var tempsString = "Temps ici"
#State var enCours = "CanadienParlementaire - Commencer"
#State var pausePlay = "pause"
#State var tempsMillieu = 420;
#State var tempsFermeture = 180
#State var round = 7;
#State var répartitionPM = "7/3"
var debatCP = CP(modePM: &répartitionPM)
#State private var showingAlert = false
//il va falloir un bouton pause
var body: some View {
VStack {
Text(String(self.round))
Text(a)
.font(.largeTitle)
.fontWeight(.semibold)
.lineLimit(nil)
Text(partie)
Button(action: {
self.showingAlert = true
if self.enCours != "Recommencer"{
let chrono = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (chrono) in
self.debatCP.verifierEtatDebut(round: &self.round, tempsActuel: &self.tempsMillieu, pause: &self.pausePlay, partie: &self.partie, tempsStr: &self.tempsString)
if self.round <= 2{
self.debatCP.verifierEtatFin(round: &self.round, tempsActuel: &self.tempsFermeture, pause: &self.pausePlay, partie: &self.partie, tempsStr: &self.tempsString)
}
if self.round <= 0 {
chrono.invalidate()
self.enCours = "Canadien Parlementaire - Commencer"
}
}
}
else{
self.tempsMillieu = 420
self.tempsFermeture = 180
self.round = 7
}
self.enCours = "Recommencer"
self.a = "CP"
}, label: {
Text(enCours)
.alert(isPresented: $showingAlert) {
Alert(title: Text("6/4 ou 7/3"), message: Text("6/4 pour avoir plus de temps à la fin et 7/3 pour en avoir plus au début"), primaryButton: .default(Text("6/4"), action: {
self.répartitionPM = "6/4";
}), secondaryButton: .default(Text("7/3"), action: {
self.répartitionPM = "7/3"
}))
}
})
I get this error message : Cannot use instance member 'répartitionPM' within property initializer; property initializers run before 'self' is available
Edit
Here's the CP class
class CP:Debat{
init(modePM: inout String){
}
override var rondeFermeture:Int{
return 2;
}
override var tempsTotalMillieu : Int{
return 420;
};
override var tempsProtege:Int{//60
return 60;
}
override var tempsLibre:Int{//360
return 360;
}
override var tempsFermeture: Int{
return 180;
}
Along with a sample of the Defat Class
class Debat{
var rondeFermeture:Int{
return 0;
}
var tempsTotalMillieu:Int{//420
return 0;
};
var tempsProtege:Int{//60
return 0;
}
var tempsLibre:Int{//360
return 0;
}
var tempsFermeture:Int{
return 0
}
func formatTime(time:Int) -> String {
let minutes:Int = time/60
let seconds:Int = time%60
return String(minutes) + ":" + String(seconds)
}
func prochainTour(time: inout Int, round : inout Int)->Void{
if round > rondeFermeture {
let tempsAvantLaFin = time % self.tempsTotalMillieu
time -= tempsAvantLaFin
}
if round <= rondeFermeture{
let tempsAvantLaFin = time % self.tempsFermeture
time -= tempsAvantLaFin
}
}
#State var répartitionPM = "7/3"
var debatCP = CP(modePM: &répartitionPM)
The SwiftUI has different pattern to work with #State variables. It is not clear what is CP but anyway the following is the way to go
struct AContentView: View {
#State var repartitionPM = "7/3" // should be defined
var debatCP:CP!=nil // declare-only
init() {
debatCP = CP(modePM: $repartitionPM) // pass as binding
}
...
and CP should be something like
class CP:Debat{
#Binding var modePM:String
init(modePM:Binding <String>){
self._modePM = modePM
super.init()
}
Make a Height struct with two variable properties, heightInInches and heightInCentimeters. Both should be of type Double.
Create two custom initializers. One initializer will take a Double argument that represents height in inches. The other initializer will take a Double argument that represents height in centimeters. Each initializer should take the passed in value and use it to set the property that corresponds to the unit of measurement passed in. It should then set the other property by calculating the right value from the passed in value. Hint: 1 inch = 2.54 centimeters.
struct Height{
var heightInInches :Double=0.0
var heightInCentimeters :Double=0.0
init(inches:Double) {
heightInInches=inches * 2.54
}
init(centimeters:Double) {
heightInCentimeters=centimeters/2.54
}
}
let inch = Height(inches:65)
print(inch.heightInInches)
let centi=Height(centimeters:65)
print(centi.heightInCentimeters)
If you use the initializer for inches to pass in a height of 65, the initializer should set heightInInches to 65 and heightInCentimeters to 165.1.
Backup for a second and take each requirement in isolation
One initializer will take a Double argument that represents height in inches.
The other initializer will take a Double argument that represents height in centimeters.
Each initializer should take the passed in value and use it to set the property that corresponds to the unit of measurement passed in.
Which might look something like...
init(inches:Double) {
heightInInches = inches
}
init(centimeters:Double) {
heightInCentimeters = centimeters
}
It should then set the other property by calculating the right value from the passed in value. Hint: 1 inch = 2.54 centimeters.
Which might look more like this...
init(inches:Double) {
heightInInches = inches
heightInCentimeters = inches * 2.54
}
init(centimeters:Double) {
heightInInches = centimeters / 2.54
heightInCentimeters = centimeters
}
This then allows you to set the properties as let and avoid all the issues with a mutating struct
struct Height{
let heightInInches: Double
let heightInCentimeters: Double
init(inches:Double) {
heightInInches = inches
heightInCentimeters = inches * 2.54
}
init(centimeters:Double) {
heightInInches = centimeters / 2.54
heightInCentimeters = centimeters
}
}
which is a lesson best left for another day ;)
I suggest you move the conversion into didSet{} methods. The problem here is that after it gets initialized, if you change heightInInches for instance, then heightInCentimeters won't change.
struct Height{
var heightInInches :Double {
didSet {
// called each time heightInInches changes
heightInCentimeters = heightInInches / 2.54
}
}
var heightInCentimeters :Double {
didSet {
// idem
heightInInches = heightInCentimeters * 2.54
}
}
init(inches:Double) {
heightInInches = inches
heightInCentimeters = inches / 2.54
}
init(centimeters:Double) {
heightInCentimeters = centimeters
heightInInches = centimeters * 2.54
}
}
Note that didSet is not called via init methods. You have to duplicate this conversion or to place it in another function that will be call in the init and the didSet.
I have 26 textfields which will provide different float values each, and I want to create a loop to make it easier & faster to add those values into a single variable.
My textfields are named a1, a2, ... a26 so the only thing that changes is the number after 'a'. How can I make swift to recognize the textfield name as I'm increasing the variable 'x'.
I was thinking something like this:
var x : Int = 1
var total : Float = 0;
var y : Float = 0;
while (x == 26) {
y = a/(x).floatValue;
total += y;
x+=1;
}
My idea is obviously not working 😕
Make an array containing all 26 text fields, and iterate over them:
let textfields = [a1, a2, ..., a26]
for textfield in textfields {
total += textfield.floatValue
}
I agree with #jtbandes' comment that it is best to start with arrays and loops, so you never need all 26 to be separate variables. If your textFields are #IBOutlets, you can use an outlet collection (which is just a single array to hold all of the textFields).
#IBOutlet var textfields: [NSTextField]!
Then you'd simply do:
for textfield in textfields {
total += textfield.floatValue
}
In the interest of showing what is possible, assuming the textfields are properties of your class (and your class derives from NSObject), you can use key-value coding to get the values:
for i in 1...26 {
if let tf = value(forKey: "a\(i)") as? NSTextField {
total += tf.floatValue
}
}
Full example for macOS:
import Cocoa
class ViewController: NSViewController {
var a1 = NSTextField()
var a2 = NSTextField()
var a3 = NSTextField()
func total() {
// give them some values for testing purposes
a1.stringValue = "1.2"
a2.stringValue = "2.3"
a3.stringValue = "3.14"
var total: Float = 0.0
for i in 1...3 {
if let tf = value(forKey: "a\(i)") as? NSTextField {
total += tf.floatValue
}
}
print(total)
}
}
ViewController().total() // 6.64
Full example for iOS:
import UIKit
class ViewController: UIViewController {
var a1 = UITextField()
var a2 = UITextField()
var a3 = UITextField()
func total() {
// give them some values for testing purposes
a1.text = "1.2"
a2.text = "2.3"
a3.text = "3.14"
var total: Float = 0.0
for i in 1...3 {
if let tf = value(forKey: "a\(i)") as? UITextField {
total += ((tf.text ?? "") as NSString).floatValue
}
}
print(total)
}
}
ViewController().total() // 6.64
After looking at different tutorial. i don't know when to use didset or get set to update the variable.
Could anyone explain a little bit more detail for when to use didset or get set?
#IBInspectable var circleColor: UIColor = UIColor.redColor() {
didSet { //after properties are set in storyboard, update here
circleLayer.strokeColor = circleColor.CGColor
self.toggleButon()
}
}
/**
Radius of RadioButton circle.
*/
#IBInspectable var circleRadius: CGFloat = 5.0
#IBInspectable var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
layer.masksToBounds = newValue > 0
}
}
for circle radius, it doesn't have to use didset to update its value. i can't get it.
Here i am giving you one example and try to explain you how to use, hope it will helpful for you.
I am using this class for UIView here for did set and get set with Story Board my class name here "MyCustomView"
import Foundation
import UIKit
import QuartzCore
/// Computed properties, based on the backing CALayer property, that are visible in Interface Builder.
#IBDesignable public class MyCustomView: UIView {
/// When positive, the background of the layer will be drawn with rounded corners. Also effects the mask generated by the `masksToBounds' property. Defaults to zero. Animatable.
#IBInspectable var cornerRadius: Double {
get {
return Double(self.layer.cornerRadius)
}
set {
self.layer.cornerRadius = CGFloat(newValue)
}
}
/// The width of the layer's border, inset from the layer bounds. The border is composited above the layer's content and sublayers and includes the effects of the `cornerRadius' property. Defaults to zero. Animatable.
#IBInspectable var borderWidth: Double {
get {
return Double(self.layer.borderWidth)
}
set {
self.layer.borderWidth = CGFloat(newValue)
}
}
/// The color of the layer's border. Defaults to opaque black. Colors created from tiled patterns are supported. Animatable.
#IBInspectable var borderColor: UIColor? {
get {
return UIColor(CGColor: self.layer.borderColor!)
}
set {
self.layer.borderColor = newValue?.CGColor
}
}
/// The color of the shadow. Defaults to opaque black. Colors created from patterns are currently NOT supported. Animatable.
#IBInspectable var shadowColor: UIColor? {
get {
return UIColor(CGColor: self.layer.shadowColor!)
}
set {
self.layer.shadowColor = newValue?.CGColor
}
}
/// The opacity of the shadow. Defaults to 0. Specifying a value outside the [0,1] range will give undefined results. Animatable.
#IBInspectable var shadowOpacity: Float {
get {
return self.layer.shadowOpacity
}
set {
self.layer.shadowOpacity = newValue
}
}
/// The shadow offset. Defaults to (0, -3). Animatable.
#IBInspectable var shadowOffset: CGSize {
get {
return self.layer.shadowOffset
}
set {
self.layer.shadowOffset = newValue
}
}
/// The blur radius used to create the shadow. Defaults to 3. Animatable.
#IBInspectable var shadowRadius: Double {
get {
return Double(self.layer.shadowRadius)
}
set {
self.layer.shadowRadius = CGFloat(newValue)
}
}
}
And you can use this with story board import this class with your "UIView"
after you will see some
and you directly set here view cornet radius, shadow and Shadow
and result you can see inside your storyboard directly without running code
Output here from this code
This answer explains the difference in usage of set vs didSet quite clearly.
IN SUMMARY
willSet should be used for doing something before the value is set. (the value is not updated at this point).
set is to update the value
didSet if you want to do anything after the value is set (the value has been updated at this point)
ALSO
if you implement set, you will also need to implement get
but didSet can also be used without having to implement any other method
#IBInspectable will work with both property kinds:
Use didSet{} for stored properties:
didSet is a property observer.
Use set{} get{} for computed properties.
in the following example: firstName And lastName are stored properties.
fullName is a Computed property:
struct Person{
var firstName:String
var lastName:String{
didSet{
//property observer
print("It was set")
}
}
var fullName:String{
get{
return "\(firstName)-\(lastName)"
}
set{
let split = newValue.split(separator: "-")
firstName = String(split.first ?? "")
lastName = String(split.last ?? "")
}
}
}
var p = Person(firstName: "Moshe", lastName: "Doe")
print(p.firstName)
print(p.lastName)
print(p.fullName)
p.fullName = "Jhon-Doe"
print(p.firstName)
print(p.lastName)
print(p.fullName)
Also look into the language guide: (Properties)
https://docs.swift.org/swift-book/LanguageGuide/Properties.html
One final note about properties and #IBInspectable:
Validation of a value may be achieved using a combo of a computed property with a stored property (Backing property): Here is an example:
//Bound between 0 and 5
var boundRating:Int = 3{
didSet{
renderRating()
}
}
#IBInspectable
var rating:Int{
set{
if newValue <= 5 && newValue >= 0{
boundRating = newValue
renderRating()
}
}
get{
return boundRating
}
}