Using state to switch SF symbol icons - swiftui

If a user taps on a photo it switches to a drawing with the following code:
import SwiftUI
struct TestView : View {
#State var photo = true
var imagePhoto = "Day-1"
var imageVector = "Dag-1"
var body: some View {
Image(photo ? imagePhoto : imageVector)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 200)
.background(Color.black)
.padding(.leading, 0)
.tapAction {
self.photo.toggle()
}
}
}
What if I want to do the same with changing icons from SF symbols? The following code doesn't work.
Image(photo ? systemName: "photo" : systemName: "pencil.circle")

You are using it wrong, this is the right syntax:
Image(systemName: photo ? "photo" : "pencil.circle")

Related

Function holds System Image rather than image

#ViewBuilder
func TabButton (image: String)-> some View{
Button {
withAnimation{currentTab = image}
} label: {
Image(image)
.resizable()
.renderingMode(.original)
.aspectRatio(contentMode: .fit)
.frame(width: 23, height: 22)
.foregroundColor(currentTab == image ? .primary : .gray)
.frame(maxWidth: .infinity)
}.buttonStyle(GradientButtonStyle())
}
}
I want rather than image from assets as input, system image whom is variable is input here and can change each time and not fixed. thank you
I use it like this,
struct MainView: View {
#State var showMenu: Bool = false
// Hiding Native One.
init(){
UITabBar.appearance().isHidden = true
}
#State var currentTab = "Home"
//offset for both drag gesture and showing menu.
#State var offset: CGFloat = 0
#State var lastStoredOffset: CGFloat = 0
var body: some View {
//let sideBarWidth = getRect().width - 90
// whole navigation view....
NavigationView{
HStack(spacing : 0){
//Side Menu
//SideMenu(showMenu: $showMenu)
//Main Tab View
VStack(spacing: 0){
TabView(selection: $currentTab){
Home(showMenu: $showMenu)
.navigationBarTitleDisplayMode(.inline)
.navigationBarHidden(true)
.tag("Home")
}
this is the tabview code up
VStack(spacing: 0)
{
Divider()
HStack(spacing:0){
// tab buttons
TabButton(image: "Home")
}
}
}
}
I want system image rather than image here , need help please!
I can only guess ... but I think you want something like this?
struct MainView: View {
// Hiding Native One.
init(){
UITabBar.appearance().isHidden = true
}
#State var currentTab = "house"
var body: some View {
//let sideBarWidth = getRect().width - 90
// whole navigation view....
NavigationView{
VStack(spacing : 0) {
//Side Menu
//SideMenu(showMenu: $showMenu)
//Main Tab View
TabView(selection: $currentTab) {
Text("Home Content") // Home(showMenu: $showMenu)
.tag("house")
Text("Star Content") // ...
.tag("star")
Text("Settings Content") // ...
.tag("gear")
}
.tabViewStyle(.page(indexDisplayMode: .never))
Divider()
Spacer(minLength: 10)
HStack {
// tab buttons
TabButton(image: "house")
TabButton(image: "star")
TabButton(image: "gear")
}
}
}
}
func TabButton (image: String)-> some View{
Button {
withAnimation{ currentTab = image }
} label: {
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 23, height: 22)
.foregroundColor( currentTab == image ? .primary : .gray)
.frame(maxWidth: .infinity)
}
// .buttonStyle(GradientButtonStyle())
}
}

KFImage image loaded but not displayed?

I am trying to display remote images using Kingfisher SDK , the images are loaded but its not displayed
import SwiftUI
import Kingfisher
struct Tab_Home: View {
//Slider
#State var sliderIndex:Int = 0
//Search bar
#State var search:String = ""
#ObservedObject var API = REST_API()
var body: some View {
VStack{
ZStack{
Group{
Rectangle().foregroundColor(Color.white.opacity(0.1))
.cornerRadius(22.5)
HStack(spacing : 0){
Image(systemName: "magnifyingglass")
.resizable()
.frame(width : 20 , height : 20)
.aspectRatio(contentMode: .fit)
.foregroundColor(Color.white)
.padding(.leading , 10)
TextFieldPro(placeholder : Text("Search").foregroundColor(.white), text: $search)
.padding(.leading , 10)
.padding(.trailing , 15)
.frame( height : 45)
.foregroundColor(Color.white)
}
}.padding(.leading , 10)
.padding(.trailing , 10)
.padding(.bottom , 5)
.padding(.top , 15)
}.frame(height : 65)
.background(Color(hex: "#CC2323"))
Spacer()
ScrollView{
TabView(selection : $sliderIndex){
ForEach(Env.sharedInstance.settings.sliders , id : \.self){ slider in
SliderBite( data: slider).frame(width :UIScreen.main.bounds.width )
}
} .tabViewStyle(PageTabViewStyle())
}
.padding(.top , 10)
.onAppear(){
API.checkin()
}
}
}
}
struct Tab_Home_Previews: PreviewProvider {
static var previews: some View {
Tab_Home()
}
}
struct SliderBite: View {
let data:DTO_SLIDER
var body: some View {
Group{
if data.full_image != nil {
KFImage(URL(string: data.full_image!)!)
.fade(duration: 0.25)
.onProgress { receivedSize, totalSize in }
.onSuccess { _ in print("Image loaded") }
.onFailure { error in print("Load image error : \(error)") }
.frame(width :UIScreen.main.bounds.width , height : 200)
.aspectRatio(contentMode: .fill)
.background(Color.black)
}
}.clipped()
}
}
//Decoded from rest API
struct DTO_SLIDER:Decodable,Hashable{
var full_image:String?
}
what did i miss there ?
The problem is not with KFImage, which is loading as expected, but with the fact that TabView is somehow unaware that the image has been loaded and that it needs to re-render it, but it can be solved easily:
Store your urls to display in an array and then use ForEach inside the TabView - this is enough to make the images appear once they are loaded:
struct TabHome: View {
#State var search: String = ""
let urls = [
"https://www.broadway.org.uk/sites/default/files/styles/banner_crop/public/2019-10/star-wars-the-rise-of-skywalker-banner-min.jpg?h=639d4ef1&itok=z4KZ-3Tt",
"https://www.broadway.org.uk/sites/default/files/styles/banner_crop/public/2019-11/star-wars-quiz-banner-min.jpg?h=f75da074&itok=bFe6rBe_"]
var body: some View {
VStack(spacing : 15) {
HStack(spacing : 10) {
Image(systemName: "magnifyingglass")
.resizable()
.frame(width: 20, height: 20)
TextField("Search", text: $search)
.frame(height: 45)
}
.padding(.horizontal)
.background(
RoundedRectangle(cornerRadius: 22.5)
.foregroundColor(Color.gray.opacity(0.1))
)
.padding()
ScrollView{
TabView {
ForEach(urls, id: \.self) { url in
SliderBite(url: url)
}
} .tabViewStyle(PageTabViewStyle())
}
}
}
}
You might also want to make your KFImage resizable:
struct SliderBite: View {
let url: String
var body: some View {
KFImage(URL(string: url)!)
.resizable()
.fade(duration: 0.25)
.aspectRatio(contentMode: .fill)
.frame(height : 200)
.background(Color.black)
}
}
To address showing of images from identical url
If you declare DTO_SLIDER like this:
struct DTO_SLIDER: Decodable, Identifiable {
let full_image: String?
let id = UUID()
}
it will ensure each one is unique even if it has identical full_image.
It also means you can use it like that:
ForEach(Env.sharedInstance.settings.sliders) { slider in
//...
}

How Can I Handle Tap Gesture on Widget?

I'm writing a widget with WidgetKit and I want to make the widget's content is clickable. For example, if users click to standings, I want to open the standings tab when the app becomes active.
I tried to use notification between the app and widget but the tap gesture is not working, I added print inside of the tap gesture but it did not appear in the console. Also, I added the same app group to both of them.
WidgetView:
struct LargeWidget : View {
#State var standings : [StandingsTable]
var body: some View {
VStack(alignment:.leading){
if standings.count > 0{
HStack(spacing:5){
Text("#").foregroundColor(.gray)
.frame(width: 30)
Text("Team".localized).foregroundColor(.gray)
Spacer()
Text("_D".localized).foregroundColor(.gray)
.frame(width: 30)
Text("_L".localized).foregroundColor(.gray)
.frame(width: 30)
Text("_W".localized).foregroundColor(.gray)
.frame(width: 30)
Text("_P".localized).foregroundColor(.gray)
.frame(width: 30)
}
Divider()
ForEach(0..<5, id: \.self) { i in
HStack(spacing:5){
Text(standings[i].rank)
.font(.system(size: 15))
.padding(.vertical, 3)
.frame(width: 30)
.background(Color(UIColor.systemBackground))
.cornerRadius(4)
Text(standings[i].name)
.lineLimit(1)
.padding(.leading, 5)
Spacer()
Text(standings[i].drawn)
.frame(width: 30)
Text(standings[i].lost)
.frame(width: 30)
Text(standings[i].won)
.frame(width: 30)
Text(standings[i].points)
.frame(width: 30)
}
.padding(.vertical, 5)
.background(standings[i].name == "Besiktas" ? Color(UIColor.systemGray6) : Color.clear)
.cornerRadius(8)
}
Spacer(minLength: 0)
}else{
Text("Large")
.padding()
}
}.padding()
.onTapGesture {
print("clicked to standings")
DispatchQueue.main.async(execute: {
NotificationCenter.default.post(name: NSNotification.Name("standings"), object: nil, userInfo: nil)
})
}
}
}
and here ContentView in app:
import SwiftUI
extension NSNotification {
static let openStandings = NSNotification.Name.init("standings")
}
struct ContentView: View {
#State var show: Bool = false
var body: some View {
NavigationView{
Text("Hello, world!")
.padding()
}.sheet(isPresented: self.$show) {
VStack{
Text("Notification")
.padding()
}
}
.onReceive(NotificationCenter.default.publisher(for: NSNotification.openStandings))
{ obj in
self.show.toggle()
}
}
}
Screenshot of Widget
Okay I created a new project with SwiftUI Environments (not old way AppDelegate and SceneDelegate).
Then I use Link for tap actions and I got it with .onOpenURL modifier in ContentView. It works :)
ContentView:
import SwiftUI
enum SelectedTab: Hashable {
case home
case standings
case options
}
struct ContentView: View {
#State var selectedTab: SelectedTab = .home
var body: some View {
TabView(selection: self.$selectedTab) {
Text("Home")
.tabItem {
Image(systemName: "house")
.renderingMode(.template)
Text("Home")
}.tag(SelectedTab.home)
Text("Standings")
.tabItem {
Image(systemName: "list.number")
.renderingMode(.template)
Text("Standings")
}.tag(SelectedTab.standings)
Text("Options")
.tabItem {
Image(systemName: "gear")
.renderingMode(.template)
Text("Options")
}.tag(SelectedTab.options)
.onOpenURL { (url) in
if url.absoluteString == "widget-deeplink://standings"{
self.selectedTab = .standings
}
}
}
}
}
Link usage example in Widget:
Link(destination: URL(string: "widget-deeplink://standings")!) {
Text("Link Test")
}

How to change the style of image with a event in swiftui?

How to change the style of image with a event in swiftui?
for example :change the image border color when I click a button, and the image is identified by name
Here is a demo
struct DemoImageBorder: View {
#State private var highlighted = false
var body: some View {
VStack {
Button("Highlight") { self.highlighted.toggle() }
Divider()
Image("some_name_here")
.border(self.highlighted ? Color.red : Color.clear)
}
}
}
how about something like this, change the color border just for the desired image name:
import SwiftUI
struct ContentView: View {
#State var borderColor = Color.blue
#State var imageName = "xyz"
var body: some View {
VStack {
Button(action: {
self.borderColor = Color.red
self.imageName = "person.3"
}){
Text("Change Image border")
}
Image(systemName: "person")
.resizable()
.frame(width: 200, height: 200)
.border(self.imageName == "person" ? borderColor : Color.blue)
Image(systemName: "person.3")
.resizable()
.frame(width: 200, height: 200)
.border(self.imageName == "person.3" ? borderColor : Color.blue)
}
}
}

How to scale the contentview to multiple screen sizes? SwiftUI

Newbie here! I am building a quiz app using Swiftui, I built the view controller by previewing it in an iPhone 11 simulator.
And I thought the controlview would fit other iPhone sizes, like iPhone 8. Because Swiftui has a built-in auto layout.
But when I run the iPhone 8 simulator some of the content in the control view is not visible because they are below the screen.
Is there a way to fix it?
I tried to play with multiple Spacer() and different paddings but I can't seem to make it look good on both screen at the same time.
This is my code:
import SwiftUI
struct questionOne: View {
#State var totalClicked: Int = 0
#State var showDetails = false
#State var isSelected = false
var body: some View {
VStack {
TRpic().frame(width: 350.0, height: 233.0).cornerRadius(10).padding(.top, 80)
Spacer()
Text(verbatim: "What's the capital of Turkey?")
.font(.title)
.padding(.bottom, 60)
.frame(height: 100.0)
Button(action: {}) {
Text("Istanbul")
}.buttonStyle(MyButtonStyle())
Spacer()
Button(action: {self.isSelected.toggle()}) {
Text("Ankara")
}.buttonStyle(SelectedButtonStyle(isSelected: $isSelected))
Spacer()
Button(action: {}) {
Text("Athens")
} .buttonStyle(MyButtonStyle())
Spacer()
NavigationLink(destination: questionTwo()) {
VStack {
Text("Next Question")
Adview().frame(width: 150, height: 60)
}
}
}.edgesIgnoringSafeArea(.top)
}
}
struct MyButtonStyle: ButtonStyle {
func makeBody(configuration:
Self.Configuration) -> some View {
configuration.label
.padding(20)
.foregroundColor(.white)
.background(configuration.isPressed ? Color.red : Color.gray)
.cornerRadius(10.0)
}
}
struct SelectedButtonStyle: ButtonStyle {
#Binding var isSelected: Bool
public func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.padding(20)
.foregroundColor(.white)
.background(isSelected ? Color.green : Color.gray)
.cornerRadius(10.0)
}
}
enter image description here
Screenshot
Being in the given context I guess you do not want a scroll view, so regarding spacing I suggest using a VStack with spacing parameter VStack(alignment: .center, spacing: n){ ... } and remove the Spacers, if between 2 views you need another distance than n, just use padding to add some extra space.
This should adjust everything to fit the height of any screen, including the image, so do not need a fixed frame for it.
But, you might have a very wide image that could go beyond safe area, so, you could set a maximum width for the image as being the screen width
struct questionOne: View {
var body: some View {
GeometryReader { geometryProxy in
VStack(alignment: .center, spacing: 20) {
TRpic().frame(maxWidth: geometryProxy.size.width, alignment: .center)
.padding([.leading, .trailing], 10)
.......
}
}
}