How to apply .italic() to .largeTitle Font? - swiftui

import SwiftUI
import PlaygroundSupport
struct ContentView: View {
var body: some View {
VStack {
ForEach(Font.TextStyle.allCases, id: \.self) { style in
Text(self.describe(style: style)).font(.system(style)).italic()
}
}
}
func describe(style: Font.TextStyle) -> String {
var s: String = ""
print(style, terminator: "", to: &s)
return s
}
}
PlaygroundPage.current.setLiveView(ContentView())
produce
is it a bug or I missed something?

Final answer from Apple
Resolution:Investigation complete - Works as currently designed
Have a lot of fun with Apple ...

Related

I can’t get the array to update across all views despite using SWIFT playgrounds own code

I’m new so please forgive me.
I’m following Swift Playgrounds tutorials on an iPad. They’re good but a little buggy.
I’ve followed the tutorial. I should end up with code that shows 5 creatures, their name and emoji. I can press add to go to another view and add more to the list. If I put a ForEach code into the add view I can see the list update. But it doesn’t seem to update in the other views. And when I navigate away and back, the update is gone.
The tutorial gets us to use append to add the item to the list. The list is called using EnvironmentObject wrapper. I’ve searched the internet and can’t find what is wrong.
Can anyone help please?
Thank you
The code walkthrough comments in the code seem to be added automatically when copying from the app.
First file : CreatureZoo
import SwiftUI
//#-learning-task(creatureZoo)
/*#-code-walkthrough(creatureZoo.observableObject)*/
class CreatureZoo : ObservableObject {
/*#-code-walkthrough(creatureZoo.observableObject)*/
/*#-code-walkthrough(creatureZoo.creatures)*/
/*#-code-walkthrough(creatureZoo.published)*/ #Published /*#-code-walkthrough(creatureZoo.published)*/var creatures = [
/*#-code-walkthrough(creatureZoo.creature)*/
Creature(name: "Gorilla", emoji: "🦍"),
/*#-code-walkthrough(creatureZoo.creature)*/
Creature(name: "Peacock", emoji: "🦚"),
Creature(name: "Squid", emoji: "🦑"),
Creature(name: "Owl", emoji: "🦉"),
Creature(name: "Unicorn", emoji: "🦄")
//#-learning-task(addCreatures)
]
/*#-code-walkthrough(creatureZoo.creatures)*/
}
/*#-code-walkthrough(creatureZoo.creatureStruct)*/
struct Creature : Identifiable {
var name : String
var emoji : String
var id = UUID()
var offset = CGSize.zero
var rotation : Angle = Angle(degrees: 0)
}
/*#-code-walkthrough(creatureZoo.creatureStruct)*/
// Should be hidden probably
extension CreatureZoo {
func randomizeOffsets() {
for index in creatures.indices {
creatures[index].offset = CGSize(width: CGFloat.random(in: -200...200), height: CGFloat.random(in: -200...200))
creatures[index].rotation = Angle(degrees: Double.random(in: 0...720))
}
}
func synchronizeOffsets() {
let randomOffset = CGSize(width: CGFloat.random(in: -200...200), height: CGFloat.random(in: -200...200))
for index in creatures.indices {
creatures[index].offset = randomOffset
}
}
func indexFor(_ creature: Creature) -> Double {
if let index = creatures.firstIndex(where: { $0.id == creature.id }) {
return Double(index)
}
return 0.0
}
}
2nd file CreatureRow
import SwiftUI
struct CreatureRow: View {
var creature : Creature
var body: some View {
HStack {
Text(creature.name)
.font(.title)
Spacer()
Text(creature.emoji)
.resizableFont()
.frame(minWidth: 125)
}
}
}
struct CreatureRow_Previews: PreviewProvider {
static var previews: some View {
CreatureRow(creature: Creature(name: "Dodo Bird", emoji: "🦤"))
}
}
3rd file CreatureEditor
import SwiftUI
import Guide
struct CreatureEditor: View {
//#-learning-task(defineVariablesCreatureEditor)
#Environment(\.dismiss) var dismiss
#State var newCreature : Creature = Creature(name: "", emoji: "")
#EnvironmentObject var data : CreatureZoo
var body: some View {
SPCAssessableGroup(view: self) {
VStack(alignment: .leading) {
Form {
Section("Name") {
//#-learning-task(addACreatureEditorTextField)
TextField("Creature Name", text: $newCreature.name)
}
Section("Emoji") {
TextField("Emoji", text: $newCreature.emoji)
}
Section("Creature Preview") {
CreatureRow(creature: newCreature)
}
Section("Current Creatures") {
ForEach(data.creatures) { creature in
CreatureRow(creature: creature)
/*#-code-walkthrough(forEach.id)*/
}
}
}
}
.toolbar {
ToolbarItem {
Button("Add") {
data.creatures.append(newCreature)
dismiss()
}
}
}
}.environmentObject(CreatureZoo()) // I added this myself to try to make things work
}
}
struct CreatureEditor_Previews: PreviewProvider {
static var previews: some View {
NavigationView() {
CreatureEditor().environmentObject(CreatureZoo())
}
}
}
4th file contentView
import SwiftUI
import Guide
struct ContentView: View {
#EnvironmentObject var data : CreatureZoo
//#-learning-task(cleanUpApp)
//#-learning-task(createYourOwn)
var body: some View {
SPCAssessableGroup(view: self) {
List {
Text("ContentView")
Section("Dance") {
NavigationLink("make the creatures dance") {
DancingCreatures()
.navigationTitle("Dancing Creatures")
.environmentObject(CreatureZoo())
}
//#-learning-task(addNavLinkCreatureDance)
}
/*#-code-walkthrough(forEach.id)*/
Section("Add new creature") {
NavigationLink(" Add new") {
CreatureEditor()
.navigationTitle("Add new")
.environmentObject(CreatureZoo())
}
}
ForEach(data.creatures) { creature in
CreatureRow(creature: creature)
/*#-code-walkthrough(forEach.id)*/
}
.onDelete { indexSet in
data.creatures.remove(atOffsets: indexSet)
}
}
.toolbar {
ToolbarItem {
NavigationLink("Add") {
CreatureEditor()
.navigationTitle("Add new creature")
.environmentObject(CreatureZoo())
}
}
}
}
}
}
5th file myApp
import SwiftUI
import Guide
//#-learning-task(myApp)
#main
/*#-code-walkthrough(myApp.appProtocol)*/
struct MyApp: App {
/*#-code-walkthrough(myApp.appProtocol)*/
#StateObject var data = CreatureZoo()
/*#-code-walkthrough(myApp.body)*/
var body: some Scene {
SPCAssessableWindowGroup(app: self, assessmentCandidates: [CreatureZoo()]) {
NavigationView {
ContentView()
.navigationTitle("My Creatures")
.environmentObject(data)
}
}
/*#-code-walkthrough(myApp.contentView)*/
/*#-code-walkthrough(myApp.contentView)*/
}
}
/*#-code-walkthrough(myApp.body)*/
When you add .environmentObject(CreatureZoo()) you're creating a new instance which is not shared between the views. You should add .environmentObject once in your App file.
In ContentView, this .environmentObject(CreatureZoo()) should be .environmentObject(data).

Flickering with .searchable List

For the example, you need to add LoremSwiftum with SPM.
I'm found that when using the .init(_:id:rowContent:) constructor of List, there is flickering of the row items in the search field.
Video of what it looks like
import SwiftUI
import LoremSwiftum
let words = Array(Set(Lorem.words(3000).components(separatedBy: " ")))
struct ContentView: View {
#State var searchText = ""
var searchedWords: [String] {
searchText.isEmpty ? words : Array(words.filter { $0.localizedCaseInsensitiveContains(searchText) }.prefix(50))
}
var body: some View {
NavigationView {
List(searchedWords, id:\.self) { word in
HStack {
Rectangle().frame(width: 50, height: 50).foregroundColor(.red)
Text(word)
}
}
.searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always))
}
}
}
Using .indices makes the flicker go away, ie:
List(searchedWords.indices, id:\.self) { i in
let word = words[I]
...
}
although as I understand it, using .indices can cause problems when the items change.
Using .id(UUID()) on the List also makes the flickering go away, although this has some problems as well.
So, how can I use the correct constructor of List and not have awful flickering of the items when searching?
Try avoiding computed properties for lists identifiable at least.
I had next object for list item:
struct Message: Identifiable, Equatable, Codable {
var id: String { UUID().uuidString }
let title: String
let message: String
}
It was flickering very similar to your example.
Changing structure to next one solved issue for me.
struct Message: Identifiable, Equatable, Codable {
let id: String
let title: String
let message: String
init(title: String, message: String) {
self.id = UUID().uuidString
self.title = title
self.message = message
}
}
I am using the following code and I don't experience the flicker.
struct ContentView: View {
#State private var words = Array(Set(Lorem.words(3000).components(separatedBy: " ")))
#State private var searchText: String = ""
#State private var filteredWords: [String] = []
var body: some View {
NavigationStack {
List(filteredWords, id: \.self) { word in
HStack {
Rectangle().frame(width: 50, height: 50).foregroundColor(.red)
Text(word)
}
}.searchable(text: $searchText)
.onChange(of: searchText) { search in
filteredWords = words.filter({ $0.starts(with: search.lowercased())})
}
.onAppear {
filteredWords = words
}
}
}
}

SwiftUI List messed up after delete action on iOS 15

It seems that there is a problem in SwiftUI with List and deleting items. The items in the list and data get out of sync.
This is the code sample that reproduces the problem:
import SwiftUI
struct ContentView: View {
#State var popupShown = false
var body: some View {
VStack {
Button("Show list") { popupShown.toggle() }
if popupShown {
MainListView()
}
}
.animation(.easeInOut, value: popupShown)
}
}
struct MainListView: View {
#State var texts = (0...10).map(String.init)
func delete(at positions: IndexSet) {
positions.forEach { texts.remove(at: $0) }
}
var body: some View {
List {
ForEach(texts, id: \.self) { Text($0) }
.onDelete { delete(at: $0) }
}
.frame(width: 300, height: 300)
}
}
If you perform a delete action on the first row and scroll to the last row, the data and list contents are not in sync anymore.
This is only happening when animation is attached to it. Removing .animation(.easeInOut, value: popupShown) workarounds the issue.
This code sample works as expected on iOS 14 and doesn't work on iOS 15.
Is there a workaround for this problem other then removing animation?
It isn't the animation(). The clue was seeing It appears that having the .animation outside of the conditional causes the problem. Moving it to the view itself corrected it to some extent. However, there is a problem with this ForEach construct: ForEach(texts, id: \.self). As soon as you start deleting elements of your array, the UI gets confused as to what to show where. You should ALWAYS use an Identifiable element in a ForEach. See the example code below:
struct ListDeleteView: View {
#State var popupShown = false
var body: some View {
VStack {
Button("Show list") { popupShown.toggle() }
if popupShown {
MainListView()
.animation(.easeInOut, value: popupShown)
}
}
}
}
struct MainListView: View {
#State var texts = (0...10).map({ TextMessage(message: $0.description) })
func delete(at positions: IndexSet) {
texts.remove(atOffsets: positions)
}
var body: some View {
List {
ForEach(texts) { Text($0.message) }
.onDelete { delete(at: $0) }
}
.frame(width: 300, height: 300)
}
}
struct TextMessage: Identifiable {
let id = UUID()
let message: String
}

Easiest way to make a dynamic, editable list of simple objects in SwiftUI?

I want a dynamic array of mutable strings to be presented by a mother view with a list of child views, each presenting one of the strings, editable. Also, the mother view will show a concatenation of the strings which will update whenever one of the strings are updated in the child views.
Can't use (1) ForEach(self.model.strings.indices) since set of indices may change and can't use (2) ForEach(self.model.strings) { string in since the sub views wants to edit the strings but string will be immutable.
The only way I have found to make this work is to make use of an #EnvironmentObject that is passed around along with the parameter. This is really clunky and borders on offensive.
However, I am new to swiftui and I am sure there a much better way to go about this, please let know!
Here's what I have right now:
import SwiftUI
struct SimpleModel : Identifiable { var id = UUID(); var name: String }
let simpleData: [SimpleModel] = [SimpleModel(name: "text0"), SimpleModel(name: "text1")]
final class UserData: ObservableObject { #Published var simple = simpleData }
struct SimpleRowView: View {
#EnvironmentObject private var userData: UserData
var simple: SimpleModel
var simpleIndex: Int { userData.simple.firstIndex(where: { $0.id == simple.id })! }
var body: some View {
TextField("title", text: self.$userData.simple[simpleIndex].name)
}
}
struct SimpleView: View {
#EnvironmentObject private var userData: UserData
var body: some View {
let summary_binding = Binding<String>(
get: {
var arr: String = ""
self.userData.simple.forEach { sim in arr += sim.name }
return arr;
},
set: { _ = $0 }
)
return VStack() {
TextField("summary", text: summary_binding)
ForEach(userData.simple) { tmp in
SimpleRowView(simple: tmp).environmentObject(self.userData)
}
Button(action: { self.userData.simple.append(SimpleModel(name: "new text"))}) {
Text("Add text")
}
}
}
}
Where the EnironmentObject is created and passed as SimpleView().environmentObject(UserData()) from AppDelegate.
EDIT:
For reference, should someone find this, below is the full solution as suggested by #pawello2222, using ObservedObject instead of EnvironmentObject:
import SwiftUI
class SimpleModel : ObservableObject, Identifiable {
let id = UUID(); #Published var name: String
init(name: String) { self.name = name }
}
class SimpleArrayModel : ObservableObject, Identifiable {
let id = UUID(); #Published var simpleArray: [SimpleModel]
init(simpleArray: [SimpleModel]) { self.simpleArray = simpleArray }
}
let simpleArrayData: SimpleArrayModel = SimpleArrayModel(simpleArray: [SimpleModel(name: "text0"), SimpleModel(name: "text1")])
struct SimpleRowView: View {
#ObservedObject var simple: SimpleModel
var body: some View {
TextField("title", text: $simple.name)
}
}
struct SimpleView: View {
#ObservedObject var simpleArrayModel: SimpleArrayModel
var body: some View {
let summary_binding = Binding<String>(
get: { return self.simpleArrayModel.simpleArray.reduce("") { $0 + $1.name } },
set: { _ = $0 }
)
return VStack() {
TextField("summary", text: summary_binding)
ForEach(simpleArrayModel.simpleArray) { simple in
SimpleRowView(simple: simple).onReceive(simple.objectWillChange) {_ in self.simpleArrayModel.objectWillChange.send()}
}
Button(action: { self.simpleArrayModel.simpleArray.append(SimpleModel(name: "new text"))}) {
Text("Add text")
}
}
}
}
You don't actually need an #EnvironmentObject (it will be available globally for all views in your environment).
You may want to use #ObservedObject instead (or #StateObject if using SwiftUI 2.0):
...
return VStack {
TextField("summary", text: summary_binding)
ForEach(userData.simple, id:\.id) { tmp in
SimpleRowView(userData: self.userData, simple: tmp) // <- pass userData to child views
}
Button(action: { self.userData.simple.append(SimpleModel(name: "new text")) }) {
Text("Add text")
}
}
struct SimpleRowView: View {
#ObservedObject var userData: UserData
var simple: SimpleModel
...
}
Note that if your data is not constant you should use a dynamic ForEach loop (with an explicit id parameter):
ForEach(userData.simple, id:\.id) { ...
However, the best results you can achieve when you make your SimpleModel a class and ObservableObject. Here is a better solution how do do it properly:
SwiftUI update data for parent NavigationView
Also, you can simplify your summary_binding using reduce:
let summary_binding = Binding<String>(
get: { self.userData.simple.reduce("") { $0 + $1.name } },
set: { _ = $0 }
)

ForEach in SwiftUI Warning Issue

I have been using SwiftUI for a few months now an I am having difficulty with using ForEach.
I am aware that the ForEach protocol demands a unique identifier, but I have used /.self to overcome that aspect of the protocol.
Now unit testing a ForEach statement but I am getting a warning which is preventing build.
Warning is Result of 'ForEach' initializer is
unused
import SwiftUI
struct GetdOrderView: View {
#State private var myFamily = ["Ufuoma","Efe","David","Vicky","Beth"]
//The use of ForEach
func myForachOne() {
ForEach((0 ... myFamily.count), id: \.self) {member in
VStack {
Text("\(member)")
}
}
}
var body: some View {
Text("Hello world")
}
}
Instead of
func myForachOne() {
Use
func myForachOne() -> some View {
//Use This
import SwiftUI
struct GetdOrderView: View {
#State private var myFamily = ["Ufuoma","Efe","David","Vicky","Beth"]
//The use of ForEach
func myForachOne() -> some View {
ForEach((0 ... myFamily.count), id: \.self) {member in
VStack {
Text("\(member)")
}
}
}
var body: some View {
Text("Hello world")
}
}