Show random images with their names from an array. (SwiftUI) - arrays

New to swiftUI, and I need to show the images and their names randomly by action of a button. How should I call the pictures' array to show a random picture each time user tap on the button in the contentView file ?
this is the array of pictures that I want to show them randomly in contentView(pictures placed in Assets folder):
struct aPicture: Identifiable {
var id: Int
var name: String
var imageName: String
}
let pictures = [
aPicture(id: 0, name: "1", imageName: "1"),
aPicture(id: 1, name: "2", imageName: "2"),
aPicture(id: 2, name: "3", imageName: "3"),
aPicture(id: 3, name: "4", imageName: "4"),
aPicture(id: 4, name: "5", imageName: "5"),
aPicture(id: 5, name: "6", imageName: "6"),
]

you can try this:
struct ContentView: View {
struct aPicture: Identifiable {
var id: Int
var name: String
var imageName: String
}
#State var random : Int = 0
let pictures = [
aPicture(id: 0, name: "1", imageName: "1"),
aPicture(id: 1, name: "2", imageName: "2"),
aPicture(id: 2, name: "3", imageName: "3"),
aPicture(id: 3, name: "4", imageName: "4"),
aPicture(id: 4, name: "5", imageName: "5"),
aPicture(id: 5, name: "6", imageName: "6"),
]
var body: some View {
VStack {
HStack {
Spacer()
Text(pictures[self.random].name)
.background(Color.white)
Spacer()
Button("Next image") {
self.random = Int.random(in: 0..<self.pictures.count)
}
Spacer()
}
Image(pictures[self.random].imageName)
.resizable()
.scaledToFit()
}
}
}

I suggest you different approach.
1) you can generate random element directly
let picture = pictures.randomElement() ?? default_if_empty_collection
2) user would like to see different picture after tap, which could not be true. Less pictures in your "store", more likely randomly you generate the same picture (which could looks like "nothing happens on tap")
next snippet shows how to solve this. On every tap the user see different picture, even though there are just tree pictures in our collection.
import SwiftUI
struct ContentView: View {
#State var img = Image(systemName: "questionmark.square.fill")
let imgs = [Image(systemName: "trash"),
Image(systemName: "trash.fill"),
Image(systemName: "trash.slash"),
]
var body: some View {
img
.resizable()
.scaledToFit()
.frame(maxWidth: .infinity)
.onTapGesture {
var tmp: Image
repeat {
tmp = self.imgs.randomElement() ?? self.img
} while tmp == self.img
self.img = tmp
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Related

Compare Two Arrays and Remove Duplicates Swift

I have two arrays and I would like to create a new array that compares the two and removes both instances of duplicates.
I have a custom object:
struct SubService: Identifiable, Hashable, Encodable {
var id: String = UUID().uuidString
var name: String
var charge: String
var price: Double
}
My two arrays:
let array1: [SubService] = [SubService(name: "Men's Haircut", charge: "service", price: 10), SubService(name: "Fade", charge: "service", price: 10)]
let array2: [SubService] = [SubService(name: "Fade", charge: "service", price: 10)]
Here is the result I'm looking for:
let result: [SubService] = [SubService(name: "Men's Haircut", charge: "service", price: 10)]
I have tried the following but it returns the same array as array1. I'm assuming because of the id?
let filteredArray = Array(Set(array1).subtracting(array2))
print statements:
ARRAY 1: [SubService(id: "F9EDBBC0-3786-4718-B6BE-C31F26D6E0F0", name: "Fade", charge: "service", price: 10.0), SubService(id: "D91939DD-C339-4A56-B09D-C19ABA56A48B", name: "Men\'s Haircut", charge: "service", price: 10.0)]
ARRAY 2: [SubService(id: "373CE5F9-ECB0-4572-BD27-8BC71F96163B", name: "Fade", charge: "service", price: 10.0)]
FILTERED ARRAY: [SubService(id: "D91939DD-C339-4A56-B09D-C19ABA56A48B", name: "Men\'s Haircut", charge: "service", price: 10.0), SubService(id: "F9EDBBC0-3786-4718-B6BE-C31F26D6E0F0", name: "Fade", charge: "service", price: 10.0)]
Any help is appreciated :)
SubService has to conform to protocol Equatable
struct SubService: Identifiable, Hashable, Encodable, Equatable {
var id: String = UUID().uuidString
var name: String
var charge: String
var price: Double
static func ==(lhs: SubService, rhs: SubService) -> Bool {
return lhs.name == rhs.name
}
}
let arrSet = Set(array2)
let filteredArray = array1.filter{ !arrSet.contains($0) }
Reuse your items instead of creating new ones for each array declaration.
let mens = SubService(name: "Men's Haircut", charge: "service", price: 10)
let womens = SubService(name: "Woman's Haircut", charge: "service", price: 10)
let array1 = [mens, womens]
let array2 = [womens]
When you redefine the second item of array1 in let array2 = ..., you create a new UUID that makes it different. You can actually see that in your printed values.

How to show array values in different forms?

I need to implement such form
I get json data from server:
"category_attributes": [
[
{
"name": "IPhone",
"terms": [
{
"name": "iPhone 11"
},
{
"name": "iPhone 12"
}
],
},
{
"name": "Memory",
"terms": [
{
"name": "1024"
},
{
"name": "128"
}
]
}
]
]
struct EachCategory: Decodable, Hashable {
let name: String
let category_attributes: [[CategoriesAttributes]]
}
struct CategoriesAttributes: Decodable, Hashable {
let name: String
let terms: [CateroriesTerms]
}
struct CateroriesTerms: Decodable, Hashable {
let name: String
}
I need to show first object's values of terms array inside buttons, and memory in dropdown list. I can only show all array values in buttons https://imgur.com/a/ZPydwmE How can I show only first object in buttons, and other objects in different form?
VStack(alignment: .leading) {
ForEach(viewModel.categoryAttributes, id: \.self) { category in
ForEach(category, id: \.self) { attribute in
Text(attribute.name)
HStack {
ForEach(attribute.terms, id: \.self) { term in
Button {
print("Phone model is selected")
} label: {
Text("\(term.name)")
.padding()
.foregroundColor(Color.textFieldGrayColor)
}.background(Color.grayButton)
.cornerRadius(10)
.frame(height: 50)
}
}
}
}
}

How to parse array data from Yelp API?

Allo, Hi, im new with SwiftUI and im facing some issue with the Yelp API and can't find answer anywhere... I'm creating a restaurant app and I want to add a on the business detail page a list of Yelp transactions that the business is registered for ("pickup", "delivery", and "restaurant_reservation").
I've try a lot of way to retrieved it but im about to give up... I don't know if it's me who's dumb or anything but my brain can't figured it out anymore. I've tried to get the data with "ForEach" and all any other way we usually get array data...
Second question (similar as the previous one) how can I retrieve the category alias/title from the API? I want to be able to filter the business based on their categories and also show on the business detail page the category associated to it.
Thank you :)
Yelp Response Body Example :
{
"total": 144,
"businesses": [
{
"id": "gR9DTbKCvezQlqvD7_FzPw",
"alias": "north-india-restaurant-san-francisco",
"price": "$$",
"url": "https://www.yelp.com/biz/north-india-restaurant-san-francisco",
"rating": 4,
"location": {
"zip_code": "94105",
"state": "CA",
"country": "US",
"city": "San Francisco",
"address2": "",
"address3": "",
"address1": "123 Second St"
},
"categories": [
{
"alias": "indpak",
"title": "Indian"
}
],
"phone": "+14153481234",
"coordinates": {
"longitude": -122.399305736113,
"latitude": 37.787789124691
},
"image_url": "http://s3-media4.fl.yelpcdn.com/bphoto/howYvOKNPXU9A5KUahEXLA/o.jpg",
"is_closed": false,
"name": "North India Restaurant",
"review_count": 615,
"transactions": ["pickup", "restaurant_reservation"]
},
// ...
]
}
Here is my Business model :
class Business: Decodable, Identifiable, ObservableObject {
#Published var imageData: Data?
var id: String?
var alias: String?
var name: String?
var imageUrl: String?
var isClosed: Bool?
var url: String?
var reviewCount: Int?
var categories: [Category]?
var rating: Double?
var coordinates: Coordinate?
var transactions: [String]?
var price: String?
var location: Location?
var phone: String?
var displayPhone: String?
var distance: Double?
enum CodingKeys: String, CodingKey {
case imageUrl = "image_url"
case isClosed = "is_closed"
case reviewCount = "review_count"
case displayPhone = "display_phone"
case id
case alias
case name
case url
case categories
case rating
case coordinates
case transactions
case price
case location
case phone
case distance
}
func getImageData() {
// Check that image url isn't nil
guard imageUrl != nil else {
return
}
// Download the data for the image
if let url = URL(string: imageUrl!) {
// Get a session
let session = URLSession.shared
let dataTask = session.dataTask(with: url) { (data, response, error) in
if error == nil {
DispatchQueue.main.async {
// Set the image data
self.imageData = data!
}
}
}
dataTask.resume()
}
}
static func getTestData() -> Business {
let b = Business()
return b
}
}
struct Category: Decodable {
var alias: String?
var title: String?
}
Here an example of my code :
struct BusinessDetail: View {
var business: Business
#State private var showDirections = false
var body: some View {
VStack (alignment: .leading) {
VStack (alignment:.leading, spacing:0) {
GeometryReader() { geometry in
// Business image
let uiImage = UIImage(data: business.imageData ?? Data())
Image(uiImage: uiImage ?? UIImage())
.resizable()
.scaledToFill()
.frame(width: geometry.size.width, height: geometry.size.height)
.clipped()
}
.ignoresSafeArea(.all, edges: .top)
// Open / closed indicator
ZStack (alignment: .leading) {
Rectangle()
.frame(height: 35)
.foregroundColor(business.isClosed! ? Color("icon-primary") : Color("background"))
Text(business.isClosed! ? "Closed" : "Open")
.foregroundColor(.white)
.font(.textHeader)
.padding(.leading)
}
}
Group {
HStack {
BusinessTitle(business: business)
.padding()
Spacer()
}
// Phone
HStack {
Text("Phone:")
.bold()
Text(business.displayPhone ?? "")
Spacer()
Link("Call", destination: URL(string: "tel:\(business.phone ?? "")")!)
}
.padding()
// Transactions
if business.transactions != nil {
ForEach(business.transactions!, id: \.self) { transaction in
Text(transaction)
.font(.bodyParagraph)
}
}

Swift how to filter 2 array

I'm new in Swift, and I can't figure out how to filter these 2 arrays
var arrayOfFavoriteRoomsId = ["1", "2"]
var arrayOfRooms = [
VoiceRoom(id: "1", title: "Room1", description:"Test room1"),
VoiceRoom(id: "2", title: "Room2", description:"Test room2"),
VoiceRoom(id: "3", title: "Room3", description:"Test room3")
]
The final array should look like this
var filteredArray = [
VoiceRoom(id: "1", title: "Room1", description:"Test room1"),
VoiceRoom(id: "2", title: "Room2", description:"Test room2")
]
This is what my model looks like
struct VoiceRoom: Identifiable, Decodable {
var id: String
var title: String
var description: String
}
arrayOfRooms.filter { room in
arrayOfFavoriteRoomsId.contains(room.id)
}
If you want to sort them as well:
arrayOfRooms.filter { room in
arrayOfFavoriteRoomsId.contains(room.id)
}.sorted(by: { $0.id < $1.id })

Swift 5 group and split array of objects based on object value

I have a sorted array
let things = [
Thing(activity: "1", name: "value1"),
Thing(activity: "1", name: "value2"),
Thing(activity: "1", name: "value3"),
Thing(activity: "2", name: "value4"),
Thing(activity: "2", name: "value5"),
Thing(activity: "3", name: "value6"),
Thing(activity: "3", name: "value7"),
Thing(activity: "1", name: "value8"),
Thing(activity: "1", name: "value9"),
Thing(activity: "1", name: "value10")
]
I would like to produce array of arrays splitted when the activity value changes similar to the following
[[Thing(activity: "1", name: "value1"),
Thing(activity: "1", name: "value2"),
Thing(activity: "1", name: "value3")],
[Thing(activity: "2", name: "value4"),
Thing(activity: "2", name: "value5")],
[Thing(activity: "3", name: "value6"),
Thing(activity: "3", name: "value7")],
[Thing(activity: "1", name: "value8"),
Thing(activity: "1", name: "value9"),
Thing(activity: "1", name: "value10")]]
A generalized solution would be:
extension Sequence {
func grouped<T: Equatable>(by block: (Element) throws -> T) rethrows -> [[Element]] {
return try reduce(into: []) { result, element in
if let lastElement = result.last?.last, try block(lastElement) == block(element) {
result[result.index(before: result.endIndex)].append(element)
} else {
result.append([element])
}
}
}
}
Then you can do:
let results = things.grouped { $0.activity }
A less elegant (but slightly more efficient) solution would be:
extension Sequence {
func grouped<T: Equatable>(by block: (Element) throws -> T) rethrows -> [[Element]] {
var results: [[Element]] = []
var lastValue: T?
var index = results.endIndex
for element in self {
let value = try block(element)
if let lastValue = lastValue, lastValue == value {
results[index].append(element)
} else {
results.append([element])
index = results.index(before: results.endIndex)
lastValue = value
}
}
return results
}
}
As already mentioned by #matt in comments you can use collection method reduce(into:) to group your elements by checking if the activity of the last element of the last array is equal to the current element activity, if so just append a new element to the last array, otherwise append a new array with a single element to the outer array:
struct Thing {
let activity, name: String
}
let things: [Thing] = [
.init(activity: "1", name: "value1"),
.init(activity: "1", name: "value2"),
.init(activity: "1", name: "value3"),
.init(activity: "2", name: "value4"),
.init(activity: "2", name: "value5"),
.init(activity: "3", name: "value6"),
.init(activity: "3", name: "value7"),
.init(activity: "1", name: "value8"),
.init(activity: "1", name: "value9"),
.init(activity: "1", name: "value10")]
let grouped: [[Thing]] = things.reduce(into: []) {
$0.last?.last?.activity == $1.activity ?
$0[$0.index(before: $0.endIndex)].append($1) :
$0.append([$1])
}
print(grouped) // "[[__lldb_expr_1.Thing(activity: "1", name: "value1"), __lldb_expr_1.Thing(activity: "1", name: "value2"), __lldb_expr_1.Thing(activity: "1", name: "value3")], [__lldb_expr_1.Thing(activity: "2", name: "value4"), __lldb_expr_1.Thing(activity: "2", name: "value5")], [__lldb_expr_1.Thing(activity: "3", name: "value6"), __lldb_expr_1.Thing(activity: "3", name: "value7")], [__lldb_expr_1.Thing(activity: "1", name: "value8"), __lldb_expr_1.Thing(activity: "1", name: "value9"), __lldb_expr_1.Thing(activity: "1", name: "value10")]]\n"

Resources