Integer Picker to select in swiftui xcode - xcode

I am making a project with qr code generator and i don't know how to use integer with picker and i want to ask what code did i missed in the function and the view. does any expert know how to solve it, thank you for the help.
my code:
#State private var sSecond = Int()
#State var navigated = false
let Second = ["10", "20", "30", "40", "50", "60"]
var body: some View {
Form{
Section {
VStack{
Picker(selection: $sSecond, label: Text("Select Seconds"))
{
ForEach(0 ..< Second.count) {
index in Text(self.Second[index]).tag(index)
}
}
}
}
NavigationLink(destination: Generate(Second: $sSecond), isActive: self.$navigated)
{
Text("Complete")
}
}
Function:
import Foundation
import SwiftUI
import CoreImage.CIFilterBuiltins
struct Generate: View {
#State var second = Int()
let filter = CIFilter.qrCodeGenerator()
let cont = CIContext()
var body: some View {
Image(uiImage: imageGenerate(second))
.interpolation(.none)
.resizable()
.frame(width: 150, height: 150, alignment: .center)
}
func imageGenerate(second: Int)-> UIImage {
let data = Data(second)
filter.setValue(data, forKey: "inputMessage")
if let qr = filter.outputImage {
if let qrImage = cont.createCGImage(qr, from: qr.extent){
return UIImage(cgImage: qrImage)
}
}
return UIImage(systemName: "xmark") ?? UIImage()
}
}
How Can I Show 10 in qr code, thats what i expected in this questions

As for your first question: Picker on Int goes like this:
struct PickerInt: View {
let secondsArray = [10, 20, 30, 40, 50, 60] // Int instead of String
#State private var sSecond: Int = 10
var body: some View {
Form{
Picker(selection: $sSecond, label: Text("Select Seconds"))
{
ForEach(secondsArray, id: \.self) { sec in
Text("\(sec)").tag(sec)
}
}
}
}
}

Related

Show a sheet in response to a drop

I'm implementing drag and drop, and have a case where I need the user to decide what to do in response to a drop. So I want to bring up a sheet to ask the user for input. The problem is that the sheet doesn't appear until I drag another item to the same view. This does make sense, so I'm looking for a way to handle this differently.
The current approach looks like this (simplified):
struct SymbolInfo {
enum SymbolType {
case string, systemName
}
var type: SymbolType
var string: String
}
struct MyView: View, DropDelegate {
#State var sheetPresented = false
#State var droppedText = ""
static let dropTypes = [UTType.utf8PlainText]
var textColor = NSColor.white
private var frameRect: CGRect = .null
private var contentPath: Path = Path()
private var textRect: CGRect = .null
#State private var displayOutput: SymbolInfo
#State private var editPopoverIsPresented = false
// There's an init to set up the display output, the various rects and path
var body: some View {
ZStack(alignment: stackAlignment) {
BackgroundView() // Draws an appropriate background
.frame(width: frameRect.width, height: frameRect.height)
if displayOutput.type == .string {
Text(displayOutput.string)
.frame(width: textRect.width, height: textRect.height, alignment: .center)
.foregroundColor(textColour)
.font(displayFont)
.allowsTightening(true)
.lineLimit(2)
.minimumScaleFactor(0.5)
}
else {
Image(systemName: displayOutput.string)
.frame(width: textRect.width, height: textRect.height, alignment: .center)
.foregroundColor(textColour)
.minimumScaleFactor(0.5)
}
}
.onAppear {
// Retrieve state information from the environment
}
.focusable(false)
.allowsHitTesting(true)
.contentShape(contentPath)
.onHover { entered in
// Populates an inspector
}
.onTapGesture(count: 2) {
// Handle a double click
}
.onTapGesture(count: 1) {
// Handle a single click
}
.popover(isPresented: $editPopoverIsPresented) {
// Handles a popover for editing data
}
.onDrop(of: dropTypes, delegate: self)
.sheet(sheetPresented: $sheetPresented, onDismiss: sheetReturn) {
// sheet to ask for the user's input
}
}
func sheetReturn() {
// act on the user's input
}
func performDrop(info: DropInfo) -> Bool {
if let item = info.itemProviders(for: dropTypes).first {
item.loadItem(forTypeIdentifier: UTType.utf8PlainText.identifier, options: nil) { (textData, error) in
if let textData = String(data: textData as! Data, encoding: .utf8) {
if (my condition) {
sheetIsPresented = true
droppedText = textData
}
else {
// handle regular drop
}
}
}
return true
}
return false
}
}
So my reasoning is that the drop sets sheetPresented to true, but then it doesn't get acted on until the view is rebuilt, such as on dragging something else to it. But I'm still new to SwiftUI, so I may be incorrect.
Is there a way to handle this kind of interaction that I haven't found?
I never was able to exactly reproduce the problem, but the issue related to trying to have more than one kind of sheet that could be shown, depending on conditions. The solution was to break up the original view into a family of views that encapsulated the different behaviours, and show the appropriate one rather than try to make one view do everything.
I won't show the whole code, since it's too deeply embedded in the app, but here's a demo app that works correctly:
import SwiftUI
import UniformTypeIdentifiers
#main
struct DragAndDropSheetApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
var body: some View {
HStack() {
TargetView(viewType: .normal, viewText: "A")
.frame(width: 40, height: 40, alignment: .top)
TargetView(viewType: .protected, viewText: "B")
.frame(width: 40, height: 40, alignment: .top)
TargetView(viewType: .normal, viewText: "C")
.frame(width: 40, height: 40, alignment: .top)
TargetView(viewType: .protected, viewText: "D")
.frame(width: 40, height: 40, alignment: .top)
}
.padding()
}
}
enum ViewType {
case normal, protected
}
struct TargetView: View, DropDelegate {
#State private var sheetPresented = false
#State var viewType: ViewType
#State var viewText: String
#State private var dropText = ""
#State private var dropType: DropActions = .none
static let dropTypes = [UTType.utf8PlainText]
var body: some View {
ZStack(alignment: .center) {
Rectangle()
.foregroundColor(viewType == .normal ? .blue : .red)
Text(viewText)
.foregroundColor(.white)
.frame(width: nil, height: nil, alignment: .center)
}
.focusable(false)
.allowsHitTesting(true)
.onDrop(of: TargetView.dropTypes, delegate: self)
.sheet(isPresented: $sheetPresented, onDismiss: handleSheetReturn) {
ProtectedDrop(isPresented: $sheetPresented, action: $dropType)
}
}
func handleSheetReturn() {
switch dropType {
case .append:
viewText += dropText
case .replace:
viewText = dropText
case .none:
// Nothing to do
return
}
}
func performDrop(info: DropInfo) -> Bool {
if let item = info.itemProviders(for: TargetView.dropTypes).first {
item.loadItem(forTypeIdentifier: UTType.utf8PlainText.identifier, options: nil) { textData, error in
if let textData = String(data: textData as! Data, encoding: .utf8) {
if viewType == .normal {
viewText = textData
}
else {
dropText = textData
sheetPresented = true
}
}
}
return true
}
return false
}
}
enum DropActions: Hashable {
case append, replace, none
}
struct ProtectedDrop: View {
#Binding var isPresented: Bool
#Binding var action: DropActions
var body: some View {
VStack() {
Text("This view is protected. What do you want to do?")
Picker("", selection: $action) {
Text("Append the dropped text")
.tag(DropActions.append)
Text("Replace the text")
.tag(DropActions.replace)
}
.pickerStyle(.radioGroup)
HStack() {
Spacer()
Button("Cancel") {
action = .none
isPresented.toggle()
}
.keyboardShortcut(.cancelAction)
Button("OK") {
isPresented.toggle()
}
.keyboardShortcut(.defaultAction)
}
}
.padding()
}
}

SwiftUI - Using a Picker on another view to sort results from API call

I have a view that makes an API call to pull nearby restaurant data and display it in a list. I've placed a button in the navigation bar of that view to display a sheet which will ultimately show the user filter options. For now, it uses a Picker placed in a form to allow the user to pick how they want to sort the results. I am using a variable with a #Binding property wrapper in the filter view to pass the selected value to a #State variable in the restaurant data view (selectedOption). It all works great until the view is reloaded. Either by going to a different view or relaunching the app. It appears the selectedOption variable in my API call in the onAppear function of the restaurant data view is being reset to what I've set the default value to when I defined the #State variable. I am wondering if there is a way to persist the value of what was chosen in the filter view through view reloads of the restaurant data view.
Restaurant data view:
import SwiftUI
import SwiftyJSON
import SDWebImageSwiftUI
struct RestaurantsView: View {
#EnvironmentObject var locationViewModel: LocationViewModel
#EnvironmentObject var venueDataViewModel: VenueDataViewModel
#State var selectedOption: String = "rating"
#State private var showingSheet = false
#State private var searchText = ""
#State private var showCancelButton: Bool = false
var body: some View {
let CPLatitude: Double = locationViewModel.lastSeenLocation?.coordinate.latitude ?? 0.00
let CPLongitude: Double = locationViewModel.lastSeenLocation?.coordinate.longitude ?? 0.00
GeometryReader { geometry in
VStack {
Text("Restaurants")
.padding()
List(venueDataViewModel.venuesListTen) { index in
NavigationLink(destination: RestaurantsDetailView(venue: index)) {
HStack {
VStack(alignment: .leading, spacing: 6) {
Text(index.name ?? "")
.font(.body)
.lineLimit(2)
Text(index.address ?? "")
.font(.subheadline)
.lineLimit(2)
}
}
}
Spacer()
if index.image != nil {
WebImage(url: URL(string: index.image ?? ""), options: .highPriority, context: nil)
.resizable()
.frame(width: 70, height: 70, alignment: .center)
.aspectRatio(contentMode: .fill)
.cornerRadius(12)
}
}
Text("Selected: \(selectedOption)")
Spacer()
}.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
showingSheet.toggle()
}, label: {
Image(systemName: "line.horizontal.3")
.imageScale(.large)
}
)
.sheet(isPresented: $showingSheet, content: {
NavigationView {
RestaurantsFilterView(selectedOption: self.$selectedOption)
}
})
}
}
.onAppear {
venueDataViewModel.retrieveVenues(latitude: CPLatitude, longitude: CPLongitude, category: "restaurants", limit: 50, sortBy: selectedOption, locale: "en_US") { (response, error) in
if let error = error {
print("\(error)")
}
}
}
}
}
}
Filter view:
import SwiftUI
struct RestaurantsFilterView: View {
#EnvironmentObject var locationViewModel: LocationViewModel
#EnvironmentObject var venueDataViewModel: VenueDataViewModel
var sortOptions = ["rating", "review_count"]
#Binding var selectedOption: String
var body: some View {
let CPLatitude: Double = locationViewModel.lastSeenLocation?.coordinate.latitude ?? 0.00
let CPLongitude: Double = locationViewModel.lastSeenLocation?.coordinate.longitude ?? 0.00
VStack {
Text("Filter")
Form {
Section {
Picker(selection: $selectedOption, label: Text("Sort")) {
ForEach(sortOptions, id: \.self) {
Text($0)
}
}.onChange(of: selectedOption) { Equatable in
venueDataViewModel.retrieveVenues(latitude: CPLatitude, longitude: CPLongitude, category: "restaurants", limit: 50, sortBy: selectedOption, locale: "en_US") { (response, error) in
if let error = error {
print("\(error)")
}
}
}
}
}
}
}
}
I am still new to Swift and SwiftUI so I appreciate the help.
Thank you!

SwiftUI URLImage Horizontal ScrollView

So I've been going through a SwiftUI instagram tutorial and learnt how to load images uploaded by user to firebase in the standard 3x3 instagram view but am now wanting to expand my knowledge and practice doing it in horizontal scrollview.
Here's what I have to create grid view:
import SwiftUI
import URLImage
import FirebaseAuth
struct Photo: Identifiable {
let id = UUID()
var photo = ""
}
struct PhotoView: View {
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
#EnvironmentObject var session: SessionStore
#ObservedObject var profileViewModel = ProfileViewModel()
var body: some View {
return
ScrollView {
if !profileViewModel.isLoading {
VStack(alignment: .leading, spacing: 1) {
// rows
ForEach(0..<self.profileViewModel.splitted.count) { index in
HStack(spacing: 1) {
// Columns
ForEach(self.profileViewModel.splitted[index], id: \.postId) { post in
URLImage(URL(string: post.mediaUrl)!,
content: {
$0.image
.resizable()
.aspectRatio(contentMode: .fill)
}).frame(width: UIScreen.main.bounds.width / 3, height: UIScreen.main.bounds.width / 3).clipped().cornerRadius(5)
}
}
}
}.frame(width: UIScreen.main.bounds.width, alignment: .leading).padding(.top, 2)
}
}.navigationBarTitle(Text("Photos"), displayMode: .inline).navigationBarBackButtonHidden(true).navigationBarItems(leading: Button(action : {
self.mode.wrappedValue.dismiss()
}) {
Image(systemName: "arrow.left")
}).onAppear {
self.profileViewModel.loadUserPosts(userId: Auth.auth().currentUser!.uid)
}
}
}
extension Array {
func splitted(into size:Int) -> [[Element]] {
var splittedArray = [[Element]]()
if self.count >= size {
for index in 0...self.count {
if index % size == 0 && index != 0 {
splittedArray.append(Array(self[(index - size)..<index]))
} else if (index == self.count) {
splittedArray.append(Array(self[index - 1..<index]))
}
}
} else {
splittedArray.append(Array(self[0..<self.count]))
}
return splittedArray
}
}
class ProfileViewModel: ObservableObject {
#Published var posts: [Post] = []
#Published var isLoading = false
var splitted: [[Post]] = []
func loadUserPosts(userId: String) {
isLoading = true
Api.User.loadPosts(userId: userId) { (posts) in
self.isLoading = false
self.posts = posts
self.splitted = self.posts.splitted(into: 3)
}
}
}
And this is what it looks like:
This is the sample code for what I am trying to achieve:
import SwiftUI
import URLImage
import FirebaseAuth
struct TestView: View {
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
var body: some View {
VStack {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 2) {
ForEach(1..<5) { _ in
Image("photo3").resizable()
.clipShape(Rectangle())
.aspectRatio(contentMode: ContentMode.fill)
.frame(width: 100, height: 100).cornerRadius(10).opacity(1).shadow(radius: 4)
}
}
}.navigationBarTitle(Text("Photos"), displayMode: .inline).navigationBarBackButtonHidden(true).navigationBarItems(leading: Button(action : {
self.mode.wrappedValue.dismiss()
}) {
Image(systemName: "arrow.left")
})
Spacer()
}.padding()
}
}
and here is the sample image of what I want it to look like:
I'm really struggling to understand the ForLoop part and how I can retrieve the image to just be in a simple scrollView.
Any help would be much appreciated!
Thanks!
You want to loop over the posts in your model. Borrowing from your earlier code, you need something like this:
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 2) {
ForEach(self.profileViewModel.posts, id: \.postId) { post in
URLImage(URL(string: post.mediaUrl)!,
content: {
$0.image
.resizable()
.aspectRatio(contentMode: .fill)
}
)
.frame(width: 100, height: 100)
.clipped()
.cornerRadius(10)
.shadow(radius: 4)
}
}
}
vacawama has already posted the perfect solution to make it look like your example.
Just to add why you achieve the result, you are getting.
The difference between your code and the sample code is that you are using two ForEach, one for the rows and one for the columns. The array gets splitted with your extension, so you get rows and columns.
//Rows
ForEach(0..<self.profileViewModel.splitted.count) { index in
HStack(spacing: 1) {
// Columns
ForEach(self.profileViewModel.splitted[index], id: \.postId) { post in
Your comments already stating how it works. If you want to have all your images in a horizontal scroller, you just need one ForEach which outputs all your images in a ScrollView.
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 2) {
ForEach(self.profileViewModel.posts, id: \.postId) { post in

SWIFT UI add element to array

I trying to learn the new SwiftUI coding technique. I would like to click a button that will add elements to an array that is a #State variable. The button is the buttonclick function. The array is the push_group_row / push_group_array. I get an error in the append statement.
Eventually the buttonclick will access a database to build an array with more row, but for now I just trying to add one row.
Code:
import SwiftUI
import Combine
var gordon: String = "xxxxxx"
struct Result: Codable {
let trackId: Int
let trackName: String
let collectionName: String
}
struct Response: Codable {
var results: [Result]
}
struct Pokemon: Identifiable {
let id: Int
let name: String
let type: String
let color: Color
}
struct push_group_row {
let id: Int
let code: String
let title: String
}
struct ContentView: View
{
#State private var results = [Result]()
#State var pokemonList = [
Pokemon(id: 0, name: "Charmander", type: "Fire", color: .red),
Pokemon(id: 1, name: "Squirtle", type: "Water", color: .blue),
Pokemon(id: 2, name: "Bulbasaur", type: "Grass", color: .green),
Pokemon(id: 3, name: "Pikachu", type: "Electric", color: .yellow),]
#State var push_group_array = [push_group_row(id: 0, code: "code12", title: "POAFire")]
var body: some View
{
NavigationView
{
VStack(alignment: . leading){
Button(action: {
// What to perform
self.buttonclick()
}) {
// How the button looks like
Text("clickme")
.background(Color.purple)
.foregroundColor(.white)
}
List(results, id: \.trackId)
{item in
NavigationLink(destination: DetailView(lm: String(item.trackId)))
{
VStack(alignment: .leading)
{
Text(String(item.trackId))
Text(item.trackName)
.font(.headline)
Text(item.collectionName)
Text(gordon)
}
}
}
List(self.pokemonList)
{ pokemon in
HStack
{
Text(pokemon.name)
Text(pokemon.type).foregroundColor(pokemon.color)
}
}
List(push_group_array, id: \.id)
{ pg_item in
HStack
{
Text(String(pg_item.id))
Text(pg_item.code)
}
}
.onAppear(perform: self.loaddata)
}
.navigationBarTitle("x")
.navigationBarItems(
trailing: Button(action: addPokemon, label: { Text("Add") }))
Spacer()
}
}
func addPokemon() {
if let randomPokemon = pokemonList.randomElement() {
pokemonList.append(randomPokemon)
}
}
// *************************** below is the add arrat code
func buttonclick() {
let newCode = "First"
let newTitle = "Second"
push_group_array.append(id: 1, code: newCode, title: newTitle)
}
func loaddata()
{
print("loaddata")
guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=song")
else
{
print("Invalid URL")
return
}
var urlData: NSData?
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data
{
if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data)
{
DispatchQueue.main.async
{
urlData = data as NSData?
self.results = decodedResponse.results
print(self.results)
print(urlData ?? "urlData_Defaultvalue")
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You need to push the object rather than 3 values
push_group_array.append(push_group_row(id: 1, code: newCode, title: newTitle))

#Published ObservedObjects SwiftUI Updates not Happening

#Published ObservedObjects SwiftUI Updates not Happening
I have created a very basic ObservableObject app and some bindings are updated
correctly and some are not. I must be missing something simple. Two views only - the
start view and a second view. I only use Text, Button and Toggle - just to test
this concept.
I want to store startup property values in UserDefaults. That part seems to work
fine. When I make changes on the second page, they are updated on that page, and the
UserDefaults are correctly written. However, on returning to the start view, the
Published bindings are not updated. Interestingly, the UserDefaults are updated.
The main view simply displays the values of the bindings and user defaults.
The start view:
import SwiftUI
struct ContentView: View {
#State private var showUtilities = false
#ObservedObject var userDefaultManager = UserDefaultsManager()
var body: some View {
NavigationView {
VStack{
Group { //group 1
Text("Published userDefaultManager.name:")
Text("\(UserUtilities().makeSubString(stringIn: self.userDefaultManager.name, count: 24))")
.padding(.bottom, 30)
.lineLimit(0)
Text("UserDefaults.standard.value:")
Text("\(UserUtilities().makeSubString(stringIn: UserDefaults.standard.value(forKey: "name") as! String, count: 24))")
.padding(.bottom, 30)
.lineLimit(0)
Text("Published userDefaultsManager.enableBiometrics:")
Text(String(self.userDefaultManager.enableBiometrics))
.padding(.bottom, 30)
.font(.headline)
} //group 1
Group { //group 2
Text("UserDefaults.standard.bool:")
Text(String(UserDefaults.standard.bool(forKey: AppDelegate.eb)))
.padding(.bottom, 30)
.font(.headline)
Button(action: {
self.showUtilities.toggle()
}) {
Text("Show Utilities")
}
.frame(width: 200)
.padding()
.font(.headline)
}//group 2
}//vstack
.navigationBarTitle("Read the Values")
.sheet(isPresented: $showUtilities) {
UserUtilities()
}
}
}
}
The second view:
import SwiftUI
import Combine
struct UserUtilities: View {
#ObservedObject var userDefaultManager = UserDefaultsManager()
var body: some View {
NavigationView {
VStack {
Toggle(isOn: self.$userDefaultManager.enableBiometrics) {
Text("Enable Biometric Login")
}
.padding(EdgeInsets(top: 50, leading: 50, bottom: 30, trailing: 50))
//this does not update
Text("Published name is \(UserUtilities().makeSubString(stringIn: self.userDefaultManager.name, count: 24))")
.padding(EdgeInsets(top: 0, leading: 0, bottom: 30, trailing: 0))
Text("UserDefaults name is \(UserUtilities().makeSubString(stringIn: UserDefaults.standard.value(forKey: "name") as! String, count: 24))")
.padding(EdgeInsets(top: 0, leading: 0, bottom: 30, trailing: 0))
\\this does not update
Text("Published enableBiometrics is " + String(self.userDefaultManager.enableBiometrics) )
Text("UserDefaults is " + String(UserDefaults.standard.bool(forKey: AppDelegate.eb)) )
.padding(.bottom, 20)
Button(action: {
self.userDefaultManager.name = UUID().uuidString
}) {
Text("Change the Published name")
}
.padding()
.font(.headline)
}
.navigationBarTitle("User Utilities", displayMode: .inline)
}
}//body
func makeSubString(stringIn: String, count: Int) -> String {
if stringIn.count > count {
let modString = stringIn.dropFirst(count)
let returnString = String(modString)
return returnString
}
return "can't get substring"
}
}
My UserDefault Manager:
import SwiftUI
import Combine
class UserDefaultsManager: ObservableObject {
#Published var name = UserDefaults.standard.value(forKey: "name") as! String {
didSet {
UserDefaults.standard.set(self.name, forKey: "name")
}
}
#Published var enableBiometrics: Bool = UserDefaults.standard.bool(forKey: AppDelegate.eb) {
didSet {
UserDefaults.standard.set(self.enableBiometrics, forKey: AppDelegate.eb)
}
}
}
And for completeness - in the AppDelegate:
static let eb = "enablebiometrics"
#ObservedObject var userDefaultManager = UserDefaultsManager()
In didFinishLaunchingWithOptions:
if UserDefaults.standard.value(forKey: AppDelegate.eb) == nil {
UserDefaults.standard.set(false, forKey: AppDelegate.eb)
}
userDefaultManager.enableBiometrics = UserDefaults.standard.bool(forKey: AppDelegate.eb)
if UserDefaults.standard.value(forKey: "name") == nil {
UserDefaults.standard.set("set in AppDelegate", forKey: "name")
}
userDefaultManager.name = UserDefaults.standard.value(forKey: "name") as! String
Any guidance would be appreciated. Xcode 11.3 (11C29)
You are using 2 Instances of your UserDefaultsManager. Instantiate it only one and pass it to the second view or add it to the environment (#EnvironmentObject) to access it in all the views.
A good example how to to this you can find at https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-environmentobject-to-share-data-between-views.
In UserUtilities, change #ObservedObject var userDefaultManager = UserDefaultsManager() to #Binding var userDefaultManager: UserDefaultsManager, and pass the userDefaultManager variable in ContentView as a parameter to UserUtilities.

Resources