Post

Replies

Boosts

Views

Activity

Reply to Horizontal List with NavigationView and NavigationLink
I've solved the problem by having NavigationView before ScrollView like the following. ZStack { VStack { NavigationView { ScrollView(.horizontal, showsIndicators: false) { HStack(alignment: .top, spacing: 0) { ForEach(horizonModels, id: \.self) { model in if model.id == 0 { NavigationLink(model.name) { MenuView0() } .font(.system(size: 20.0)) .padding(.horizontal, 20.0) .foregroundColor(Color.white) } else { NavigationLink(model.name) { MenuView1() } .font(.system(size: 20.0)) .padding(.horizontal, 20.0) .foregroundColor(Color.white) } } } } .frame(height: 40.0) .background(Color.orange) } } } That's kind of odd to me.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Dec ’21
Reply to Publishers.CombineLatest in SwiftUI
I could do something like the following. class ValidateLogin { var good: Bool = false let user: String let pass: String init(user: String, pass: String) { self.user = user self.pass = pass } func validateMe() -> Bool { if user.count > 3 && pass.count > 3 { good = true } return good } } struct ContentView: View { @State var userText: String = "" @State var passText: String = "" @State var canSave: Bool = false var body: some View { ZStack { VStack { TextField("Username", text: $userText) { }.onChange(of: userText) { newValue in let validateLogin = ValidateLogin(user: userText, pass: passText) canSave = validateLogin.validateMe() } SecureField("Password", text: $passText) { }.onChange(of: passText) { newValue in let validateLogin = ValidateLogin(user: userText, pass: passText) canSave = validateLogin.validateMe() } }.padding(.horizontal, 20.0) }.onAppear { //Publishers.CombineLatest($userText, $passText) } } } The code above doesn't involve Combine at all. I want to do it in a Combine way.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jan ’22
Reply to Publishers.CombineLatest in SwiftUI
I guess ObservableObject is a ticket to using Combine in SwiftUI. So I can write the following. import SwiftUI import Combine struct ContentView: View { @State var cancellables = Set<AnyCancellable>() @StateObject var login = Login() @State var canSave: Bool = false var body: some View { VStack { Text("Login") TextField("Enter username", text: $login.user) TextField("Enter password", text: $login.pass) Button("Save") { } .foregroundColor(canSave ? Color.orange : Color.gray) .font(.system(size: 40.0)) .disabled(!canSave) } .padding(.horizontal, 40.0) .onAppear { Publishers.CombineLatest(login.$user, login.$pass) .sink { completion in print(completion) } receiveValue: { (result0, result1) in let bool = (result0.count > 3 && result1.count > 3) canSave = bool }.store(in: &cancellables) } } } class Login: ObservableObject { @Published var user: String = "" @Published var pass: String = "" } This is really good stuff.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jan ’22
Reply to Showing Alert or Sheet after Some Delay?
I've figured out a way of doing it with StateObject. It's something like the following. import SwiftUI import Combine struct ContentView2: View { @State var disabled: Bool = false @StateObject var delayMonitor = DelayMonitor() @State private var showingAlert = false var body: some View { VStack { Spacer() Button("Tap to connect me") { disabled = true delayMonitor.start() } .font(.system(size: 24.0)) .disabled(disabled) Spacer() .frame(height: 30.0) }.onChange(of: delayMonitor.failed) { newValue in print("You've failed?: \(newValue)") disabled = !newValue showingAlert = newValue } .alert("Something is wrong...", isPresented: $showingAlert) { Button("OK", role: .cancel) { } } } } class DelayMonitor: ObservableObject { var timer = Timer() var seconds: Double = 0.0 @Published var failed: Bool = false func start() { timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { _ in self.seconds += 1.0 if self.seconds == 5.0 { // arbitrary timeout self.timer.invalidate() DispatchQueue.main.async() { [weak self] in guard let strongSelf = self else { return } strongSelf.failed = true } } }) } } The onChange guy will let me know only if the value (delayMonitor.failed) has changed. Since its initial value is set to false, I'll get a call only if it changes to true.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jan ’22
Reply to Picker with Unexpected Results
I've solved both of the issues with the following. struct PickColorView: View { @State var numberIndex: Int = 4 @State var textSizeIndex: Int = 0 let numbers: [Int] = [14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60] var body: some View { NavigationView { Form { Picker(selection: $numberIndex, label: Text("Numbers")) { ForEach((0...20), id: \.self) { Text("\($0)") } }.onChange(of: numberIndex) { newIndex in print("Index: \(newIndex)") } Picker("Numbers", selection: $textSizeIndex) { ForEach(0..<numbers.endIndex) { index in let number = numbers[index] Text("\(number)") } }.onChange(of: textSizeIndex) { newIndex in print("Index: \(newIndex)") } } .navigationBarTitle("Settings") .navigationBarHidden(false) } .navigationViewStyle(StackNavigationViewStyle()) } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jan ’22
Reply to Where and How to Create FileManager as a Singleton?
I've figured it out. You use UIApplicationDelegateAdaptor for iOS, NSApplicationDelegateAdaptor for Cocoa. import SwiftUI @main struct MyCrazyApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: UIResponder, UIApplicationDelegate { let fileManager = FileManager.default } In this fashion, you will access to fileManager in AppDelegate inside another View.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Feb ’22
Reply to 'animation' was deprecated in iOS 15.0
I guess I've somehow worked it out as follows. import SwiftUI struct ContentView5: View { @State private var larger = true var body: some View { VStack { Circle() .fill(Color.pink) .frame(width: 150, height: 150) .scaleEffect(larger ? 2 : 1) .animation(.easeInOut(duration: 1).repeatForever(), value: larger) }.onAppear { larger = false } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Feb ’22
Reply to Telling a View to show a Dialog from Another
I guess I've solved my question as follows. import SwiftUI struct ContentView: View { @ObservedObject var monster: MonsterObservable var body: some View { GeometryReader { geo in ZStack { HStack(spacing: 0.0) { LeftView() .frame(width: geo.size.width / 2.0, height: geo.size.height, alignment: .leading) RightView(showMe: $monster.showDialog) .frame(width: geo.size.width / 2.0, height: geo.size.height, alignment: .trailing) } ShowDialogView(isShowing: monster.showDialog) { } .frame(width: 500, height: 600, alignment: .center) .cornerRadius(10.0) } } } } class MonsterObservable: ObservableObject { @Published var showDialog = false } import SwiftUI struct RightView: View { @Binding var showMe: Bool var body: some View { ZStack { Color.red Button { showMe = true } label: { Text("Tap me") .font(.largeTitle) } } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Mar ’22
Reply to Passing a Variable Between Two Unrelated Views Without Binding
I guess I've solved a problem on my own again. import SwiftUI struct ContentView: View { @StateObject var monster = MonsterObservable.shared var body: some View { GeometryReader { geo in ZStack { HStack(spacing: 0.0) { RightView() .frame(width: geo.size.width, height: geo.size.height) } ShowDialogView(isShowing: $monster.showDialog) { } .frame(width: 500, height: 600, alignment: .center) .cornerRadius(10.0) } } } } struct ShowDialogView<Content: View>: View { @Binding var isShowing: Bool @ViewBuilder let content: () -> Content var body: some View { Group { if isShowing { ZStack { Color.brown VStack { Spacer() Button { isShowing = false } label: { Text("Close me") .font(.largeTitle) }.padding([.top, .bottom], 100.0) } } } } } } class MonsterObservable: ObservableObject { static let shared = MonsterObservable() @Published var showDialog = false } class GameScore: ObservableObject { static let shared = GameScore() @Published var score = 0 } struct RightView: View { @ObservedObject var monster = MonsterObservable.shared var body: some View { ZStack { Color.red Button { monster.showDialog = true } label: { Text("Change") .font(.largeTitle) } } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Mar ’22
Reply to Getting an Updated Value Through @EnvironmentObject
import SwiftUI struct ContentView: View { @EnvironmentObject var gameSettings: GameSettings var body: some View { GeometryReader { geo in ZStack { HStack(spacing: 0.0) { RightView().environmentObject(gameSettings) .frame(width: geo.size.width / 2.0, height: geo.size.height) Spacer() } VStack { HStack { Spacer() Button { print("\(gameSettings.score)") } label: { Text("Print") .font(.largeTitle) }.padding(.trailing, 40.0) } Spacer() } } } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Mar ’22
Reply to Horizontal List with NavigationView and NavigationLink
I've solved the problem by having NavigationView before ScrollView like the following. ZStack { VStack { NavigationView { ScrollView(.horizontal, showsIndicators: false) { HStack(alignment: .top, spacing: 0) { ForEach(horizonModels, id: \.self) { model in if model.id == 0 { NavigationLink(model.name) { MenuView0() } .font(.system(size: 20.0)) .padding(.horizontal, 20.0) .foregroundColor(Color.white) } else { NavigationLink(model.name) { MenuView1() } .font(.system(size: 20.0)) .padding(.horizontal, 20.0) .foregroundColor(Color.white) } } } } .frame(height: 40.0) .background(Color.orange) } } } That's kind of odd to me.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Dec ’21
Reply to Publishers.CombineLatest in SwiftUI
I could do something like the following. class ValidateLogin { var good: Bool = false let user: String let pass: String init(user: String, pass: String) { self.user = user self.pass = pass } func validateMe() -> Bool { if user.count > 3 && pass.count > 3 { good = true } return good } } struct ContentView: View { @State var userText: String = "" @State var passText: String = "" @State var canSave: Bool = false var body: some View { ZStack { VStack { TextField("Username", text: $userText) { }.onChange(of: userText) { newValue in let validateLogin = ValidateLogin(user: userText, pass: passText) canSave = validateLogin.validateMe() } SecureField("Password", text: $passText) { }.onChange(of: passText) { newValue in let validateLogin = ValidateLogin(user: userText, pass: passText) canSave = validateLogin.validateMe() } }.padding(.horizontal, 20.0) }.onAppear { //Publishers.CombineLatest($userText, $passText) } } } The code above doesn't involve Combine at all. I want to do it in a Combine way.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Jan ’22
Reply to Publishers.CombineLatest in SwiftUI
I guess ObservableObject is a ticket to using Combine in SwiftUI. So I can write the following. import SwiftUI import Combine struct ContentView: View { @State var cancellables = Set<AnyCancellable>() @StateObject var login = Login() @State var canSave: Bool = false var body: some View { VStack { Text("Login") TextField("Enter username", text: $login.user) TextField("Enter password", text: $login.pass) Button("Save") { } .foregroundColor(canSave ? Color.orange : Color.gray) .font(.system(size: 40.0)) .disabled(!canSave) } .padding(.horizontal, 40.0) .onAppear { Publishers.CombineLatest(login.$user, login.$pass) .sink { completion in print(completion) } receiveValue: { (result0, result1) in let bool = (result0.count > 3 && result1.count > 3) canSave = bool }.store(in: &cancellables) } } } class Login: ObservableObject { @Published var user: String = "" @Published var pass: String = "" } This is really good stuff.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Jan ’22
Reply to Showing Alert or Sheet after Some Delay?
I've figured out a way of doing it with StateObject. It's something like the following. import SwiftUI import Combine struct ContentView2: View { @State var disabled: Bool = false @StateObject var delayMonitor = DelayMonitor() @State private var showingAlert = false var body: some View { VStack { Spacer() Button("Tap to connect me") { disabled = true delayMonitor.start() } .font(.system(size: 24.0)) .disabled(disabled) Spacer() .frame(height: 30.0) }.onChange(of: delayMonitor.failed) { newValue in print("You've failed?: \(newValue)") disabled = !newValue showingAlert = newValue } .alert("Something is wrong...", isPresented: $showingAlert) { Button("OK", role: .cancel) { } } } } class DelayMonitor: ObservableObject { var timer = Timer() var seconds: Double = 0.0 @Published var failed: Bool = false func start() { timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { _ in self.seconds += 1.0 if self.seconds == 5.0 { // arbitrary timeout self.timer.invalidate() DispatchQueue.main.async() { [weak self] in guard let strongSelf = self else { return } strongSelf.failed = true } } }) } } The onChange guy will let me know only if the value (delayMonitor.failed) has changed. Since its initial value is set to false, I'll get a call only if it changes to true.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Jan ’22
Reply to Any way to prevent macOS from repositioning my window?
You can do it if you hide the title bar.
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
Boosts
Views
Activity
Jan ’22
Reply to Picker with Unexpected Results
I've solved both of the issues with the following. struct PickColorView: View { @State var numberIndex: Int = 4 @State var textSizeIndex: Int = 0 let numbers: [Int] = [14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60] var body: some View { NavigationView { Form { Picker(selection: $numberIndex, label: Text("Numbers")) { ForEach((0...20), id: \.self) { Text("\($0)") } }.onChange(of: numberIndex) { newIndex in print("Index: \(newIndex)") } Picker("Numbers", selection: $textSizeIndex) { ForEach(0..<numbers.endIndex) { index in let number = numbers[index] Text("\(number)") } }.onChange(of: textSizeIndex) { newIndex in print("Index: \(newIndex)") } } .navigationBarTitle("Settings") .navigationBarHidden(false) } .navigationViewStyle(StackNavigationViewStyle()) } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Jan ’22
Reply to Syntax-highlighting a String with Multiple Keys
I've ended up creating a UIViewRepresentable struct so that I can set an NSAttributedString object to a UILabel object.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Feb ’22
Reply to UIStepper in Catalyst?
The availability of UIStepper in Mac Catalyst depends on the deployment target. According to the doc, it requires iOS 13.0 or higher.
Topic: App & System Services SubTopic: Hardware Tags:
Replies
Boosts
Views
Activity
Feb ’22
Reply to How to move objects to the right side?
HStack + Spacer() should work unless you have NavigationLink inside the list.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Feb ’22
Reply to Where and How to Create FileManager as a Singleton?
I've figured it out. You use UIApplicationDelegateAdaptor for iOS, NSApplicationDelegateAdaptor for Cocoa. import SwiftUI @main struct MyCrazyApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: UIResponder, UIApplicationDelegate { let fileManager = FileManager.default } In this fashion, you will access to fileManager in AppDelegate inside another View.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Feb ’22
Reply to 'animation' was deprecated in iOS 15.0
I guess I've somehow worked it out as follows. import SwiftUI struct ContentView5: View { @State private var larger = true var body: some View { VStack { Circle() .fill(Color.pink) .frame(width: 150, height: 150) .scaleEffect(larger ? 2 : 1) .animation(.easeInOut(duration: 1).repeatForever(), value: larger) }.onAppear { larger = false } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Feb ’22
Reply to Telling a View to show a Dialog from Another
It's trivial as follows. import SwiftUI struct LeftView: View { var body: some View { ZStack { Color.green } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Mar ’22
Reply to Telling a View to show a Dialog from Another
I guess I've solved my question as follows. import SwiftUI struct ContentView: View { @ObservedObject var monster: MonsterObservable var body: some View { GeometryReader { geo in ZStack { HStack(spacing: 0.0) { LeftView() .frame(width: geo.size.width / 2.0, height: geo.size.height, alignment: .leading) RightView(showMe: $monster.showDialog) .frame(width: geo.size.width / 2.0, height: geo.size.height, alignment: .trailing) } ShowDialogView(isShowing: monster.showDialog) { } .frame(width: 500, height: 600, alignment: .center) .cornerRadius(10.0) } } } } class MonsterObservable: ObservableObject { @Published var showDialog = false } import SwiftUI struct RightView: View { @Binding var showMe: Bool var body: some View { ZStack { Color.red Button { showMe = true } label: { Text("Tap me") .font(.largeTitle) } } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Mar ’22
Reply to Passing a Variable Between Two Unrelated Views Without Binding
I guess I've solved a problem on my own again. import SwiftUI struct ContentView: View { @StateObject var monster = MonsterObservable.shared var body: some View { GeometryReader { geo in ZStack { HStack(spacing: 0.0) { RightView() .frame(width: geo.size.width, height: geo.size.height) } ShowDialogView(isShowing: $monster.showDialog) { } .frame(width: 500, height: 600, alignment: .center) .cornerRadius(10.0) } } } } struct ShowDialogView<Content: View>: View { @Binding var isShowing: Bool @ViewBuilder let content: () -> Content var body: some View { Group { if isShowing { ZStack { Color.brown VStack { Spacer() Button { isShowing = false } label: { Text("Close me") .font(.largeTitle) }.padding([.top, .bottom], 100.0) } } } } } } class MonsterObservable: ObservableObject { static let shared = MonsterObservable() @Published var showDialog = false } class GameScore: ObservableObject { static let shared = GameScore() @Published var score = 0 } struct RightView: View { @ObservedObject var monster = MonsterObservable.shared var body: some View { ZStack { Color.red Button { monster.showDialog = true } label: { Text("Change") .font(.largeTitle) } } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Mar ’22
Reply to Getting an Updated Value Through @EnvironmentObject
import SwiftUI struct ContentView: View { @EnvironmentObject var gameSettings: GameSettings var body: some View { GeometryReader { geo in ZStack { HStack(spacing: 0.0) { RightView().environmentObject(gameSettings) .frame(width: geo.size.width / 2.0, height: geo.size.height) Spacer() } VStack { HStack { Spacer() Button { print("\(gameSettings.score)") } label: { Text("Print") .font(.largeTitle) }.padding(.trailing, 40.0) } Spacer() } } } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Mar ’22