SwiftUI context menu not passing correct variable - contextmenu

I would expect that sheet that is presented show the actual string that was pressed in the list.
i.e, long press G and the presenting sheet should show G
Unfortunately it doesn't, I'm assuming this is a SwiftUI bug.
Does anyone have a clean solution for this?
Thanks
struct ContentView: View {
let items = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
#State var showing = false
var body: some View {
NavigationView {
List {
ForEach(items, id: \.self) { item in
Text(item).font(.largeTitle)
.contextMenu {
self.contextEdit(item)
}
}
}
}
}
private func contextEdit(_ item: String) -> some View {
Button(action: {
self.showing.toggle()
print(item)
}) {
Text("Edit")
Image(systemName: "circle")
}.sheet(isPresented: $showing) {
Text(item)
}
}
}

sheets should only be used on the top level. This causes unexpected behaviour as the warnings in your output should also say.
Here is a working example:
struct ContentView: View {
let items = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
#State var showing = false
#State var currentItem: String = ""
var body: some View {
NavigationView {
List {
ForEach(items, id: \.self) { item in
Text(item).font(.largeTitle)
.contextMenu {
Button(action: {
self.currentItem = item
self.showing.toggle()
}) {
Text("Edit")
Image(systemName: "circle")
}
}
}
}
}.sheet(isPresented: self.$showing) {
Text(self.currentItem)
}
}
}
I hope this helps
Greetings krjw

After a lot of trail and error I found that his works well for my needs
struct PresentingContextItem<Destination: View>: View {
#State private var showingDestination: Bool = false
var destination: () -> Destination
let title: String
let image: String
init(title: String, image: String, #ViewBuilder _ destination: #escaping () -> Destination) {
self.destination = destination
self.title = title
self.image = image
}
var body: some View {
Button(action: {
self.showingDestination.toggle()
}) {
Text(self.title)
Image(systemName: self.image)
}.sheet(isPresented: self.$showingDestination) {
self.destination()
}
}
}
Usage
struct ContentView: View {
let items = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
var body: some View {
NavigationView {
List(items, id: \.self) { item in
Text(item).font(.largeTitle)
.contextMenu {
// option 1
PresentingContextItem(title: "Create", image: "circle") {
Text("Edit...\(item)").foregroundColor(.red)
}
// option 2
self.starItem(item)
}
}
}
}
private func starItem(_ item: String) -> some View {
PresentingContextItem(title: "Star", image: "star") {
Text("Star...\(item)").foregroundColor(.green)
}
}
}

Related

Multiple List with SwiftUI

Let say That I have an Array of arrays like so:
var arrays = [["one", "two", "three", "Four"], ["four", "five", "six"]]
My goal is to create a list for each child of each subarray then navigate to the next child.
Example 1: one -> (NavigationLink) two -> (NavigationLink) three -> (NavigationLink) Four.
Example 2: four -> (NavigationLink) five -> (NavigationLink) six.
Each arrow is a NavigationLink between each List.
Here's what I've tried so far:
struct ContentView: View {
let arrays = [["A", "B", "C", "D"], ["E", "F", "G", "H"]]
var body: some View {
NavigationView {
List {
ForEach(arrays, id: \.self) { item in
NavigationLink( destination: DestinationView(items: item)) {
Text(item.first!).font(.subheadline)
}
}
}
.navigationBarTitle("Dash")
}
}
}
struct DestinationView : View {
var items: [String]
var body: some View {
List {
ForEach(items, id: \.self) { item in
NavigationLink( destination: DestinationView(items: items)) {
Text(item).font(.subheadline)
}
}
}
}
}
You can do it like this by slicing the array each time you pass it to the DestinationView
import SwiftUI
struct ContentView: View {
let arrays = [["A", "B", "C", "D"], ["E", "F", "G", "H"]]
var body: some View {
NavigationView {
List {
ForEach(arrays, id: \.self) { item in
NavigationLink( destination: DestinationView(items: Array(item[1..<item.count]))) {
Text(item.first!).font(.subheadline)
}
}
}
.navigationBarTitle("Dash")
}
}
}
struct DestinationView : View {
var items: [String]
var body: some View {
List {
if self.items.count > 0 {
NavigationLink( destination: DestinationView(items: Array(items[1..<items.count]))) {
Text(items[0]).font(.subheadline)
}
}
}
}
}

How to animate removal transition of multiple rows from SwiftUI List?

Please see this sample view that demonstrates the problem:
struct ListRemovalTransition: View {
let list1 = ["A", "B", "C", "D"]
let list2 = ["A", "E", "F", "G", "H", "I", "J", "K"]
#State var toggle = false
var chosenList: [String] {
toggle ? list1 : list2
}
var body: some View {
VStack {
Toggle(isOn: $toggle) {
Text("Switch List")
}
List(chosenList, id: \.self) { item in
Text(item)
.transition(AnyTransition.opacity.animation(.default))
}
}
.padding()
}
}
struct ListRemovalTransition_Previews: PreviewProvider {
static var previews: some View {
ListRemovalTransition()
}
}
The desired outcome is that the individual rows fade out when removed without changing position. Instead what's happening seems like all the rows first overlap each other before being removed. I've added a transition with animation to the row Text but this has no impact.
Just add id(:) modifier to List to remove default animation. Then add transition(:) modifier to List for your desirable transition. It works perfectly. I just tested on Xcode 11.5. Here is my code...
struct ListRemovalTransition: View {
let list1 = ["A", "B", "C", "D"]
let list2 = ["A", "E", "F", "G", "H", "I", "J", "K"]
#State var toggle = false
var chosenList: [String] {
toggle ? list1 : list2
}
var body: some View {
VStack {
Toggle(isOn: $toggle) {
Text("Switch List")
}
List(chosenList, id: \.self) { item in
Text(item)
}
.id(UUID())
.transition(AnyTransition.opacity.animation(.default))
}
.padding()
}
}
https://media.giphy.com/media/dVu1CMqk3YdtZHefaE/giphy.gif
Thanks. X_X

How to set up a more maintainable way of doing things based on multiple UISwitch states?

So I'm currently creating a password generator and setting up every combination of if statements checking what switches are enabled and do something based on that is annoying to code and maintain.
#objc private func mainButtonAction() {
mainLabel.text = ""
if secondViewController.lowercaseLettersToggle.isOn == true && secondViewController.uppercaseLettersToggle.isOn == false && secondViewController.numbersToggle.isOn == false && secondViewController.specialCharactersToggle.isOn == false {
let lowercaseOnlyArray = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
mainLabel.text?.append(lowercaseOnlyArray.randomElement()!)
} else if secondViewController.lowercaseLettersToggle.isOn == false && secondViewController.uppercaseLettersToggle.isOn == true && secondViewController.numbersToggle.isOn == false && secondViewController.specialCharactersToggle.isOn == false {
let uppercaseOnlyArray = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
mainLabel.text?.append(uppercaseOnlyArray.randomElement()!)
} else if secondViewController.lowercaseLettersToggle.isOn == false && secondViewController.uppercaseLettersToggle.isOn == false && secondViewController.numbersToggle.isOn == true && secondViewController.specialCharactersToggle.isOn == false {
let numberOnlyArray = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
mainLabel.text?.append(numberOnlyArray.randomElement()!)
} else if secondViewController.lowercaseLettersToggle.isOn == false && secondViewController.uppercaseLettersToggle.isOn == false && secondViewController.numbersToggle.isOn == false && secondViewController.specialCharactersToggle.isOn == true {
let specialCharactersOnlyArray = ["`", "~", "!", "#", "#", "$", "%", "^", "&", "*", "(", ")", "-", "_", "+", "=", "[", "{", "]", "}", "\\", "|", ";", ":", "'", "\"", ",", "<", ".", ">", "/", "?"]
mainLabel.text?.append(specialCharactersOnlyArray.randomElement()!)
} else if secondViewController.lowercaseLettersToggle.isOn == true && secondViewController.uppercaseLettersToggle.isOn == true && secondViewController.numbersToggle.isOn == false && secondViewController.specialCharactersToggle.isOn == false {
let lowercaseAndUppercaseArray = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
mainLabel.text?.append(lowercaseAndUppercaseArray.randomElement()!)
}else if secondViewController.lowercaseLettersToggle.isOn == true && secondViewController.uppercaseLettersToggle.isOn == false && secondViewController.numbersToggle.isOn == true && secondViewController.specialCharactersToggle.isOn == false {
let lowercaseAndNumberArray = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
mainLabel.text?.append(lowercaseAndNumberArray.randomElement()!)
} else if secondViewController.lowercaseLettersToggle.isOn == true && secondViewController.uppercaseLettersToggle.isOn == false && secondViewController.numbersToggle.isOn == false && secondViewController.specialCharactersToggle.isOn == true {
let lowercaseAndSpecialCharactersArray = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "`", "~", "!", "#", "#", "$", "%", "^", "&", "*", "(", ")", "-", "_", "+", "=", "[", "{", "]", "}", "\\", "|", ";", ":", "'", "\"", ",", "<", ".", ">", "/", "?"]
mainLabel.text?.append(lowercaseAndSpecialCharactersArray.randomElement()!)
}
UIPasteboard.general.string = mainLabel.text
mainButton.alpha = CGFloat(1)
}
So as you see here you can see how confusing it is to read every if statement and how annoying it is to set every if statement up (I haven't even set every if statement up here because I'm here to find a way where I wouldn't have to do this). Does anybody have a solution to this?
Start with an empty array. Now check each switch one at a time — not using else, just a sequence of four if statements. Each time one of those is true, append the characters corresponding to that choice. When you've passed thru all four if statements, you will have all the characters in the array appropriate to that configuration. Now pick a random element from the array.
Pseudocode:
var array = [Character]()
if lowercase { append lowercaseChars }
if uppercase { append uppercaseChars }
if numbers { append numberChars }
if special { append specialChars }
pick a char

Multiple picker views: unexpectedly found nil while unwrapping an Optional value

I'm trying to create a view controller with multiple pickerviews using the following code:
import UIKit
import Firebase
import FirebaseAuth
class DormViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var dormName: UIPickerView!
#IBOutlet weak var entryway: UIPickerView!
#IBOutlet weak var roomNumber: UIPickerView!
#IBOutlet weak var nextButton: UIButton!
var ref: DatabaseReference!
let dorms = ["Adams", "Apley", "Cabot", "Canaday", "Currier", "DeWolfe 10", "Dewolfe 20", "Dunster", "Eliot", "Fairfax", "Grays", "Greenough", "Hollis", "Holworthy", "Hurlbut", "Inn at Harvard", "Kirkland", "Leverett Towers", "Leverett McKinlock Hall", "Lionel", "Lowell", "Massachusetts Hall", "Mather Lowrise", "Mather Tower", "Matthews", "Mower", "New Quincy", "Pennypacker", "Pforzheimer", "Pforzheimer Jordan North", "Pforzheimer Jordan South", "Stone Hall", "Stoughton", "Straus", "Thayer", "Weld", "Wigglesworth", "Winthrop" ]
let entrances = ["N/A", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
let rooms = ["N/A", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99"]
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
dormName = UIPickerView()
entryway = UIPickerView()
roomNumber = UIPickerView()
dormName.delegate = self
dormName.dataSource = self
entryway.delegate = self
entryway.dataSource = self
roomNumber.delegate = self
roomNumber.dataSource = self
dormName.tag = 0
entryway.tag = 1
roomNumber.tag = 2
// Do any additional setup after loading the view.
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView.tag == 1 {
return dorms[row]
}
else if pickerView.tag == 2 {
return entrances[row]
}
else {
return rooms[row]
}
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView.tag == 1 {
return dorms.count
}
else if pickerView.tag == 2 {
return entrances.count
}
else {
return rooms.count
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let user = Auth.auth().currentUser
if let user = user {
let uid = user.uid
var entry = entrances[row]
var num = rooms[row]
if entrances[row] == "N/A" {
entry = ""
}
if rooms[row] == "N/A" {
num = ""
}
self.ref.child("Dorms").child(uid).setValue(["Location": dorms[row] + " " + entry + "-" + num ])
}
else {
print("error")
}
}
#IBAction func nextButton(_ sender: Any) {
performSegue(withIdentifier: "tohomepage", sender: self)
}
}
However, I'm getting an error:
fatal error: unexpectedly found nil while unwrapping an Optional value
In my viewDidLoad() function, when I make the call:
dormName.delegate = self
I've tried deleting the delegate and dataSource calls, and the same error still happens. I also tried deleting the tags and referencing the picker views by their names, as in:
pickerview == "dormName"
But then I get an index out of range error.
What am I doing wrong?
when you drag UIPickerView from xib or storyboad you don't need call some init :
dormName = UIPickerView()
entryway = UIPickerView()
roomNumber = UIPickerView()
delete it , and try again
In your didSelectRow you do not check for the picker instance anymore and just access the data of all arrays by the same row index. This will cause an index out of range error as soon as you pick an item with an index that goes beyond the smallest array length (in this case, entrances).
Also, looks like you have your tags/indices wrong. E.g. you set the tag of dormName to 0, but in your code you access the dorm data when the tag equals 1 (which in fact refers to the entryway picker).

Swift: generate an array of (Swift) characters

Simple question - hopefully, I am trying to generate a simple array of characters, something in the vein of:
// trying to do something like this (pseudo code):
let letters:[Character] = map(0..<26) { i in 'a' + i }
and have tried the following to no avail
let a = Character("a")
let z = Character("z")
let r:Range<Character> = a..<z
let letters:[Character] = map(a..<z) { i in i }
I realize that Swift uses Unicode, what is the correct way to do something like this?
(Note, this is not a question about interop with legacy Obj-C char, strictly in Swift for testing etc).
It's a little cumbersome to get the initial character code (i.e. 'a' in c / Obj-C) in Swift, but you can do it like this:
let aScalars = "a".unicodeScalars
let aCode = aScalars[aScalars.startIndex].value
let letters: [Character] = (0..<26).map {
i in Character(UnicodeScalar(aCode + i))
}
Thanks for useful answers.
I'm using one-liner version.
let xs = (97...122).map({Character(UnicodeScalar($0))})
or
let xs = (0..<26).map({Character(UnicodeScalar("a".unicodeScalars.first!.value + $0))})
Xcode 12.5 • Swift 5.4
extension ClosedRange where Bound == Unicode.Scalar {
static let asciiPrintable: ClosedRange = " "..."~"
var range: ClosedRange<UInt32> { lowerBound.value...upperBound.value }
var scalars: [Unicode.Scalar] { range.compactMap(Unicode.Scalar.init) }
var characters: [Character] { scalars.map(Character.init) }
var string: String { String(scalars) }
}
extension String {
init<S: Sequence>(_ sequence: S) where S.Element == Unicode.Scalar {
self.init(UnicodeScalarView(sequence))
}
}
let characters = ("a"..."z").characters // "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
let string = ("a"..."z").string // "abcdefghijklmnopqrstuvwxyz"
let range = ClosedRange.asciiPrintable // {lowerBound " ", upperBound "~"} 32...126
let characters = range.characters // [" ", "!", """, "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "#", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~"]
let string = range.string // " !"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
If you just want an array of a known set:
let str = "abcdefghijklmnopqrstuvwxyz"
let characterArray = Array(str)
println(characterArray)
//[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z]
With Swift 5, you can use the following Playground sample code in order to get an array of characters from a range of Unicode scalars:
// 1.
let unicodeScalarRange: ClosedRange<Unicode.Scalar> = "A" ... "Z"
// 2.
let unicodeScalarValueRange: ClosedRange<UInt32> = unicodeScalarRange.lowerBound.value ... unicodeScalarRange.upperBound.value
// 3.
let unicodeScalarArray: [Unicode.Scalar] = unicodeScalarValueRange.compactMap(Unicode.Scalar.init)
// 4.
let characterArray: [Character] = unicodeScalarArray.map(Character.init)
print(characterArray)
/*
prints: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
*/
Create a range of Unicode scalars that match the code points for uppercased latin alphabet Unicode block.
Because this first range is not strideable (you can't iterate on it), convert it to a range of Unicode scalar numeric representations using Unicode.Scalar's value property.
Iterate over your range of Unicode scalar numeric representations in order to create an array of Unicode scalars.
Iterate over your array of Unicode scalars in order to create an array of characters.
As an alternative, you can use one of the sample codes below if you need to start from a range of Characters or a range of Strings:
let unicodeScalarRange: ClosedRange<Character> = "A" ... "Z"
let unicodeScalarValueRange = unicodeScalarRange.lowerBound.unicodeScalars[unicodeScalarRange.lowerBound.unicodeScalars.startIndex].value ... unicodeScalarRange.upperBound.unicodeScalars[unicodeScalarRange.lowerBound.unicodeScalars.startIndex].value
let unicodeScalarArray = unicodeScalarValueRange.compactMap(Unicode.Scalar.init)
let characterArray = unicodeScalarArray.map(Character.init)
print(characterArray)
/*
prints: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
*/
let unicodeScalarRange: ClosedRange<String> = "A" ... "Z"
let unicodeScalarValueRange = unicodeScalarRange.lowerBound.unicodeScalars[unicodeScalarRange.lowerBound.unicodeScalars.startIndex].value ... unicodeScalarRange.upperBound.unicodeScalars[unicodeScalarRange.lowerBound.unicodeScalars.startIndex].value
let unicodeScalarArray = unicodeScalarValueRange.compactMap(Unicode.Scalar.init)
let characterArray = unicodeScalarArray.map(Character.init)
print(characterArray)
/*
prints: ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
*/
(11...36).map { String($0 - 1, radix: $0) }
Swift 5, patch for #Mike S's
let aScalars = "a".unicodeScalars
let aCode = aScalars[aScalars.startIndex].value
let letters: [Character] = (0..<26).map {
i in
Character(Unicode.Scalar(aCode + i) ?? aScalars[aScalars.startIndex])
}
Details
Xcode Version 10.3 (10G8), Swift 5
Solution 1
// MAKR: - ClosedRange extensions
extension ClosedRange where Bound == Unicode.Scalar {
var representationRange: ClosedRange<UInt32> { return lowerBound.value...upperBound.value }
var scalars: [Bound] { return representationRange.compactMap(Bound.init) }
}
extension ClosedRange where Bound == Character {
var scalars: [Unicode.Scalar]? {
guard lowerBound.unicodeScalars.count == 1, upperBound.unicodeScalars.count == 1 else { return nil }
return (lowerBound.unicodeScalars.first! ... upperBound.unicodeScalars.first!).scalars
}
var all: [Bound]? { return scalars?.map(Character.init) }
}
extension ClosedRange where Bound == String {
var scalars: [Unicode.Scalar]? {
guard lowerBound.unicodeScalars.count == 1, upperBound.unicodeScalars.count == 1,
let first = lowerBound.first, let last = upperBound.first else { return nil }
return (first...last).scalars
}
var all: [Bound]? { return scalars?.map(String.init) }
}
// MAKR: - Array extensions
extension Array where Element == Character {
init?(_ range: ClosedRange<Element>) {
guard let array = range.all else { return nil }
self = array
}
}
extension Array where Element == String {
init?(_ range: ClosedRange<Element>) {
guard let array = range.all else { return nil }
self = array
}
}
extension Array where Element == Unicode.Scalar { init(_ range: ClosedRange<Element>) { self = range.scalars } }
Usage 1
func test(value: Any) { print("-- \(type(of: value)) : \(value)") }
print("====================")
test(value: ("a"..."z").scalars ?? [])
test(value: ("a"..."z").all ?? [])
test(value: ("aa"..."z").all ?? [])
test(value: ("a"..."zz").all ?? [])
print("====================")
test(value: (Character("a")...Character("z")).scalars ?? [])
test(value: (Character("a")...Character("z")).all ?? [])
print("====================")
test(value: (Unicode.Scalar("a")...Unicode.Scalar("z")).scalars)
print("====================")
test(value: [Unicode.Scalar]("a"..."z"))
test(value: [Character]("a"..."z") ?? [])
test(value: [String]("a"..."z") ?? [])
test(value: [String]("aa"..."z") ?? [])
test(value: [String]("a"..."zz") ?? [])
Usage 1 log
====================
-- Array<Scalar> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<String> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<String> : []
-- Array<String> : []
====================
-- Array<Scalar> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<Character> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
====================
-- Array<Scalar> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
====================
-- Array<Scalar> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<Character> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<String> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<String> : []
-- Array<String> : []
Solution 2
extension Unicode.Scalar: Strideable {
public typealias Stride = Int
public func distance(to other: Unicode.Scalar) -> Stride { return abs(Int(value) - Int(other.value)) }
public func advanced(by n: Stride) -> Unicode.Scalar { return Unicode.Scalar(value + UInt32(n)) ?? self }
}
extension Character: Strideable {
public typealias Stride = Int
public func distance(to other: Character) -> Stride {
guard unicodeScalars.count == 1, other.unicodeScalars.count == 1 else { return 0 }
return unicodeScalars.first!.distance(to: other.unicodeScalars.first!)
}
public func advanced(by n: Stride) -> Character {
guard unicodeScalars.count == 1 else { return self }
return Character(unicodeScalars.first!.advanced(by: n))
}
}
extension Array where Element == String {
init?(_ range: ClosedRange<Element>) {
guard range.lowerBound.unicodeScalars.count == 1, range.upperBound.unicodeScalars.count == 1,
let first = range.lowerBound.unicodeScalars.first, let last = range.upperBound.unicodeScalars.first else { return nil }
self = [Unicode.Scalar](first...last).map(String.init)
}
}
Usage 2
func test(value: Any) { print("-- \(type(of: value)) : \(value)") }
test(value: [Unicode.Scalar]("a"..."z"))
test(value: [Character]("a"..."z"))
test(value: [String]("a"..."z"))
test(value: Array("a"..."z"))
test(value: Array(Character("a")...Character("z")))
test(value: Array(Unicode.Scalar("a")...Unicode.Scalar("z")))
Usage 2 log
-- Array<Scalar> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<Character> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Optional<Array<String>> : Optional(["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"])
-- Optional<Array<String>> : Optional(["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"])
-- Array<Character> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
-- Array<Scalar> : ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
Solving for:
// trying to do something like this (pseudo code):
// let letters:[Character] = map(0..<26) { i in 'a' + i }
with Swift 4.2 / 5.0:
extension Character: Strideable {
public typealias Stride = Int
// https://stackoverflow.com/questions/39982335/creating-a-countableclosedrangecharacter
public func distance(to other: Character) -> Character.Stride {
let stride = Int(String(self).unicodeScalars.first!.value) - Int(String(other).unicodeScalars.first!.value)
return abs(stride)
}
public func advanced(by n: Character.Stride) -> Character {
return Character(UnicodeScalar(String(self).unicodeScalars.first!.value + UInt32(n))!)
}
}
extension ClosedRange where Element == Character {
var characters: [Character] { return Array(self) }
}
yields:
let letters: [Character] = ("A"..."Z").characters
print(letters)
// ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
This is a small refinement to #Mike S's answer.
You can simply get the ASCII value of the first character in the English alphabet and iterate as follows.
let aScalars = ("a" as Character).asciiValue!
let letters: [Character] = (0..<26).map {
i in Character(UnicodeScalar(aScalars + i))
}
Complexity
Runtime: O(n)
Memory: O(n)
Where n is the total letters (Character) in the
English alphabet.
You can simply use
let alphabets: [UnicodeScalar] = Array("A"..."Z")

Resources