How to make Image Clickable in swiftUI? - ios

import SwiftUI
struct ContentView: View {
#State private var isActive : Bool = false
var body: some View {
VStack {
Image("home-img")
.resizable()
.aspectRatio(contentMode:.fill)
.edgesIgnoringSafeArea(.top)
.edgesIgnoringSafeArea(.bottom)
.overlay(Image("round")
.resizable()
.frame(width: 350, height: 350 , alignment: .center)
.overlay(Image("girl-img")
.resizable()
.frame(width: 150, height: 150, alignment: .center)
)
.overlay(Image("video")
.resizable()
.frame(width: 100, height: 100)
.offset(y: -200)
.padding(.bottom, -70)
).onTapGesture(count: 1) {
NavigationView {
NavigationLink(destination: SelecteImageView(), isActive: self.$isActive) {
Text("")
}
}
}
}
}
}
What I want to do is when I tap on Image("Video") it should redirect me to a new screen using NavigationView. Normally, with button, it redirects to a new screen, but here the problem is that the image is in overlay. Thank you in advance.

You need to put your Image Views inside the NavigationLink label.
The navigationLink acts like Button and it gets the default button style with blue color. So change the default button style to plain using the bellow modifier:
import SwiftUI
struct ContentView: View {
#State private var isActive : Bool = false
var body: some View {
NavigationView {
NavigationLink(destination: SelecteImageView(), isActive: self.$isActive) {
VStack {
Image("home-img") //.renderingMode(.original)
.resizable()
.aspectRatio(contentMode:.fill)
.edgesIgnoringSafeArea(.top)
.edgesIgnoringSafeArea(.bottom)
.clipped() //Here shows your Image only inside the frame
.overlay(Image("round") //.renderingMode(.original)
.resizable()
.frame(width: 350, height: 350 , alignment: .center))
.overlay(Image("girl-img") //.renderingMode(.original)
.resizable()
.frame(width: 150, height: 150, alignment: .center)
)
.overlay(Image("video") //.renderingMode(.original)
.resizable()
.frame(width: 100, height: 100)
.offset(y: -200)
.padding(.bottom, -70)
)
}
}.buttonStyle(PlainButtonStyle())
}
}
}
Or, you can give your Image views .renderingMode(.original) modifier if you are only using Images, and remove .buttonStyle(PlainButtonStyle()) from above code.

Please add tap gesture over here
struct Contentview: View {
#State var isNavigate :Bool = false
var body: some View {
NavigationView{
VStack{
NavigationLink(destination: LoginView()) {
Image("Birthday")
.resizable()
.aspectRatio(contentMode:.fill)
.edgesIgnoringSafeArea(.top)
.edgesIgnoringSafeArea(.bottom)
.frame(width: 200, height: 200 , alignment: .center)
.overlay(Image("round")
.resizable()
.frame(width: 350, height: 350 , alignment: .center)
)
.overlay(Image("girl-img")
.resizable()
.frame(width: 150, height: 150, alignment: .center)
)
}
}
}
}
}

Related

burger menu layout issues in swiftUI

how to make is that when i press the burger menu it doesn't move the other content around it
i am using a button with an if statement in a ZStack to create a menu like effect from the side
import SwiftUI
struct ContentView: View {
#State private var showMenu = false
var body: some View {
VStack {
HStack {
Image("logo")
.resizable()
.frame(width: 164, height: 34)
.padding(15)
Spacer()
ZStack {
if showMenu{
Text("About")
.foregroundColor(.white)
.frame(width: UIScreen.main.bounds.width/2)
}
Button {
showMenu.toggle()
}label: {
Image("menu")
.resizable()
.frame(width: 30, height: 30)
.padding(15)
}
}
.background(Color.black.opacity(showMenu ? 0.7 : 0))
.animation(.default)
.edgesIgnoringSafeArea(.all)
.onTapGesture {
showMenu = false
}
}
VStack {
Text("Hire The World's Top Calibers")
.font(.system(size: 47))
.fontWeight(.semibold)
Image("image")
.resizable()
.frame(width: 500, height: 400)
Spacer()
HStack {
Button {
//code
}label: {
Rectangle()
.fill(.black)
.frame(width: 150, height: 70)
.overlay(
Text("Hire a Caliber")
.foregroundColor(.white)
)
}
.padding()
Button {
//code
}label: {
Rectangle()
.fill(.white)
.border(Color.black, width: 2)
.frame(width: 150, height: 70)
.overlay(
Text("Join as a Caliber")
.foregroundColor(.black)
)
}
.padding()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.preferredColorScheme(.light)
}
}
it was originally in a HStack and i tried putting it in a ZStack but that didn't work
i tried looking for answers on chatGPT and this is the best that i got

SwiftUI simple view, need a push into right direction

I am complete beginner with SwiftUI and I can't wrap my head around how to connect these images with views that represents lines. Now I simply have 3 VStacks with image and text and put them into a HStack, but don't know how to connect these images with a line shown in red in the picture I attached. Note that there's some space between the line and the image. I need general direction and some hints, full working code not necessary.
Thank you.
How's this?
In SwiftUI, you use HStacks and VStacks to stack your Views. For the red line, a Rectangle should do. Here's the code:
struct ContentView: View {
var body: some View {
HStack { /// horizontal stack
VStack {
Image(systemName: "face.smiling")
.font(.system(size: 80))
.padding()
.border(Color.black, width: 5)
Text("Text TEXTEXT")
}
Rectangle()
.fill(Color.red)
.frame(height: 5)
VStack {
Image(systemName: "face.smiling")
.font(.system(size: 80))
.padding()
.border(Color.black, width: 5)
Text("Text TEXTEXT")
}
Rectangle()
.fill(Color.red)
.frame(height: 5)
VStack {
Image(systemName: "face.smiling")
.font(.system(size: 80))
.padding()
.border(Color.black, width: 5)
Text("Text TEXTEXT")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.previewLayout(.fixed(width: 800, height: 200))
}
}
Version 1.0.0
I decided to give my answer which is same like aheze answer with this difference that you can have CustomVerticalAlignment as well! As I see in your Image in question you want that also:
with CustomVerticalAlignment: In center!
without CustomVerticalAlignment: off center!
import SwiftUI
struct ContentView: View {
var body: some View {
HStack(alignment: .customVerticalAlignment) {
VStack {
Image(systemName: "star")
.resizable()
.scaledToFit()
.frame(width: 50, height: 50, alignment: .center)
.padding()
.border(Color.black, width: 5)
.alignmentGuide(.customVerticalAlignment) { d in d[VerticalAlignment.center] }
Text("Text")
}
Capsule()
.fill(Color.red)
.frame(height: 5)
.alignmentGuide(.customVerticalAlignment) { d in d[VerticalAlignment.center] }
VStack {
Image(systemName: "star")
.resizable()
.scaledToFit()
.frame(width: 50, height: 50, alignment: .center)
.padding()
.border(Color.black, width: 5)
.alignmentGuide(.customVerticalAlignment) { d in d[VerticalAlignment.center] }
Text("Text")
}
Capsule()
.fill(Color.red)
.frame(height: 5)
.alignmentGuide(.customVerticalAlignment) { d in d[VerticalAlignment.center] }
VStack {
Image(systemName: "star")
.resizable()
.scaledToFit()
.frame(width: 50, height: 50, alignment: .center)
.padding()
.border(Color.black, width: 5)
.alignmentGuide(.customVerticalAlignment) { d in d[VerticalAlignment.center] }
Text("Text")
}
}
.padding()
}
}
extension VerticalAlignment {
struct CustomVerticalAlignment: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
d[VerticalAlignment.center]
}
}
static let customVerticalAlignment = VerticalAlignment(CustomVerticalAlignment.self)
}
Update Version 2.0.0
About this version: I would say it does the same job of version 1.0.0 in less code and also Text and Line are not depending on VStack or eachother any moere!
import SwiftUI
struct ContentView: View {
var body: some View {
HStack {
image.overlay(text.offset(y: 40), alignment: .bottom)
capsule
image.overlay(text.offset(y: 40), alignment: .bottom)
capsule
image.overlay(text.offset(y: 40), alignment: .bottom)
}
.padding(50)
}
var image: some View {
return Image(systemName: "star.fill")
.resizable()
.scaledToFit()
.padding(10)
.shadow(radius: 10)
.frame(width: 50, height: 50, alignment: .center)
.foregroundColor(Color.red)
.background(Color.yellow)
.border(Color.black, width: 5)
}
var capsule: some View {
return Capsule()
.fill(Color.red)
.frame(height: 5)
}
var text: some View {
return Text("Hello World!")
.lineLimit(1)
.fixedSize()
}
}
You could define a Shape that represents your line.
I used the spacing parameter of HStack to do the spacing:
struct MyLine : Shape {
func path(in rect: CGRect) -> Path {
Path { path in
path.move(to: CGPoint(x: 0, y: rect.midY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.midY))
}
}
}
struct ContentView: View {
var body: some View {
HStack(spacing: 10) {
VStack {
Image(systemName: "pencil")
Text("Label")
}
MyLine().stroke(Color.red)
VStack {
Image(systemName: "pencil")
Text("Label 2")
}
MyLine().stroke(Color.red)
VStack {
Image(systemName: "pencil")
Text("Label 3")
}
}
}
}
You could add a lineWidth parameter to make the stroke thicker:
.stroke(Color.red, lineWidth: 4)
Also, if you didn't using spacing on the HStack, you could using a padding modifier on either the VStacks or the MyLines to get the spacing.

Going back to Start Screen leads into stacking the views in each other

Everytime I am going back to the start screen it seems that it stacks itself. You can see what i mean in the screenshot i attached. I added some borders so you can see what i mean
The code of the Start screen:
struct StartScreen: View {
var body: some View {
NavigationView{
VStack() {
Image("Headline").resizable().scaledToFit()
Image("GreenMonster")
.resizable()
.scaledToFit()
.frame(alignment: .top)
NavigationLink(destination: Game(monster: monster)) {
Text("Spielen")
.frame(width: 200, height: 50, alignment: .center)
.font(.title)
.padding()
.background(Color.blue)
.cornerRadius(40)
.foregroundColor(.white)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 40)
.stroke(Color.blue, lineWidth: 5)
)
}.isDetailLink(false)
/*
NavigationLink(destination: Settings()){
Image("Settingswheel").resizable().scaledToFit().frame(width: 50, height: 50).offset(x: 150)
}
*/
}
}.navigationBarBackButtonHidden(true).border(Color.green)
}
}
and the code to go back is:
struct DefeatedView: View {
#EnvironmentObject var helper: Helper
var body: some View {
NavigationView(){
VStack(){
Text("BESIEGT!").foregroundColor(.green).font(.title).bold()
Image(monster[0].imageURL).resizable().scaledToFit()
NavigationLink(destination: StartScreen()){
Text("Zum Start").frame(width: 120, height: 6, alignment: .center)
.padding()
.background(Color.blue)
.cornerRadius(40)
.foregroundColor(.white)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 40)
.stroke(Color.blue, lineWidth: 5)
)
}
}
}.navigationBarBackButtonHidden(true)
}
}
Thanks for the help i am new into SwiftUI
Add this to your DefeatedView
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
And then instead of using a NavigationLink again, use a button and push your View manually back to your Start View
Button(action: {
//Push navigation view back
self.presentationMode.wrappedValue.dismiss()
})
{
Text("Zum Start").frame(width: 120, height: 6, alignment: .center)
.padding()
.background(Color.blue)
.cornerRadius(40)
.foregroundColor(.white)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 40)
.stroke(Color.blue, lineWidth: 5)
)
}
Edit:
As you pushed your NavigationView twice, calling presentation mode only once will indeed push back to your Game View. Here is a possible solution with ObservableObject.
class ViewHelper : ObservableObject
{
#Published var finishedGame : Bool = false
}
struct StartScreen: View {
#EnvironmentObject var viewHelper : ViewHelper
var body: some View {
NavigationLink(destination: Game(), isActive: self.$viewHelper.finishedGame) {
Text("Spielen")
And then when the gameplay Is finished, change the finishedGame variable.
struct DefeatedView: View {
#EnvironmentObject var viewHelper : ViewHelper
var body: some View {
NavigationView(){
VStack(){
Text("BESIEGT!").foregroundColor(.green).font(.title).bold()
Button(action: {
self.viewHelper.finishedGame = false
})
{
Text("Zum Start").frame(width: 120, height: 6, alignment: .center)
.padding()
.background(Color.blue)
.cornerRadius(40)
.foregroundColor(.white)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 40)
.stroke(Color.blue, lineWidth: 5)
)
}
}
}.navigationBarBackButtonHidden(true)
I found the solution to my Problem on this Site:
https://thinkdiff.net/ios/swiftui-how-to-pop-to-root-view/

Add new item in Scroll View in SwiftUI

I'm trying to add a new view inside the Scroll View that contains a button every time that I click in the blue button in the bottom
]1
Here i create the scroll view with 2 buttons, and want to add more after I click in the button on the right
HStack{
ScrollView(.horizontal, content: {
HStack{
PageStep()
PageStep()
}
})
Button(action: {
self.addNewStep = true
}) {
Image(systemName: "plus.square")
.frame(width: 75, height: 75)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.blue, lineWidth: 5)
)
}.buttonStyle(PlainButtonStyle()).padding(.trailing, 10)
}
.padding(.leading, 10)
.frame(minWidth: 0, maxWidth: UIScreen.main.bounds.width, minHeight: 80, alignment: .bottom)
.foregroundColor(Color.blue)
struct PageStep: View {
var stepPossition = String()
var body: some View {
Button(action: {
print("Entrou")
}){
Rectangle()
.fill(Color.red)
.frame(width: 75, height: 75)
.cornerRadius(10)
}
}
}
Here is possible approach. Tested with Xcode 11.4.
struct ContentView: View {
#State private var steps = 2 // pages counter
var body: some View {
HStack{
ScrollView(.horizontal, content: {
HStack{
// create available pages
ForEach(0..<steps, id: \.self) { i in
PageStep(stepPossition: "\(i)").id(i) // inject
}
}
})
Button(action: {
self.steps += 1 // << add next page
}) {
Image(systemName: "plus.square")
.frame(width: 75, height: 75)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.blue, lineWidth: 5)
)
}.buttonStyle(PlainButtonStyle()).padding(.trailing, 10)
}
.padding(.leading, 10)
.frame(minWidth: 0, maxWidth: UIScreen.main.bounds.width, minHeight: 80, alignment: .bottom)
.foregroundColor(Color.blue)
}
}
struct PageStep: View {
var stepPossition: String
var body: some View {
Button(action: {
print("Entrou")
}){
Rectangle()
.fill(Color.red)
.frame(width: 75, height: 75)
.cornerRadius(10)
}
}
}

SwiftUI - Button with Image is clickable outside

I have a ScrollView with multiple Buttons. A Button contains a Image and a Text underneath.
As the images are pretty large I am using .scaledToFill and .clipped. And it seems that the 'clipped' part of the image is still clickable even if it's not shown.
In the video you see I am clicking on button 1 but button 2 is triggered.
This is my Coding. The Image is inside the View Card.
struct ContentView: View {
#State var useWebImage = false
#State var isSheetShowing = false
#State var selectedIndex = 0
private let images = [
"https://images.unsplash.com/photo-1478368499690-1316c519df07?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2706&q=80",
"https://images.unsplash.com/photo-1507154258-c81e5cca5931?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2600&q=80",
"https://images.unsplash.com/photo-1513310719763-d43889d6fc95?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2734&q=80",
"https://images.unsplash.com/photo-1585766765962-28aa4c7d719c?ixlib=rb-1.2.1&auto=format&fit=crop&w=2734&q=80",
"https://images.unsplash.com/photo-1485970671356-ff9156bd4a98?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2734&q=80",
"https://images.unsplash.com/photo-1585607666104-4d5b201d6d8c?ixlib=rb-1.2.1&auto=format&fit=crop&w=2700&q=80",
"https://images.unsplash.com/photo-1577702066866-6c8897d06443?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2177&q=80",
"https://images.unsplash.com/photo-1513809491260-0e192158ae44?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2736&q=80",
"https://images.unsplash.com/photo-1582092723055-ad941d1db0d4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2700&q=80",
"https://images.unsplash.com/photo-1478264635837-66efba4b74ba?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjF9&auto=format&fit=crop&w=2682&q=80"
]
var body: some View {
NavigationView {
ScrollView {
VStack(spacing: 40) {
Text(useWebImage ? "WebImage is used." : "SwiftUI Image is used")
.font(.system(size: 18))
.bold()
.kerning(0.5)
.padding(.top, 20)
Toggle(isOn: $useWebImage) {
Text("Use WebImage")
.font(.system(size: 18))
.bold()
.kerning(0.5)
.padding(.top, 20)
}
ForEach(0..<images.count) { index in
Button(action: {
self.selectedIndex = index
self.isSheetShowing.toggle()
}) {
Card(imageUrl: self.images[index], index: index, useWebImage: self.$useWebImage)
}
.buttonStyle(PlainButtonStyle())
}
}
.padding(.horizontal, 20)
.sheet(isPresented: self.$isSheetShowing) {
DestinationView(imageUrl: self.images[self.selectedIndex], index: self.selectedIndex, useWebImage: self.$useWebImage)
}
}
.navigationBarTitle("Images")
}
}
}
struct Card: View {
let imageUrl: String
let index: Int
#Binding var useWebImage: Bool
var body: some View {
VStack {
if useWebImage {
WebImage(url: URL(string: imageUrl))
.resizable()
.indicator(.activity)
.animation(.easeInOut(duration: 0.5))
.transition(.fade)
.scaledToFill()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 250, maxHeight: 250, alignment: .center)
.cornerRadius(12)
.clipped()
} else {
Image("image\(index)")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 250, maxHeight: 250, alignment: .center)
.cornerRadius(12)
.clipped()
}
HStack {
Text("Image #\(index + 1) (\(useWebImage ? "WebImage" : "SwiftUI Image"))")
.font(.system(size: 18))
.bold()
.kerning(0.5)
Spacer()
}
}
.padding(2)
.border(Color(.systemRed), width: 2)
}
}
Do you have an idea how to fix this issue?
I already tried to use .resizable(resizingMode: .tile) but I need to shrink the image before I could use just a tile.
For detailed information you can also find the project on GitHub GitHub Project
I would appreciate your help a lot.
The .clipped affects only drawing, and by-default Button has all content clickable not depending what it is.
So if you want make your button clickable only in image area, you have to limit hit testing only to its rect explicitly and disable everything else.
Here is a demo of possible approach. Tested with Xcode 11.4 / iOS 13.4.
Demo code (simplified variant of your snapshot):
struct ButtonCard: View {
var body: some View {
VStack {
Image("sea")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 250, maxHeight: 250, alignment: .center)
.cornerRadius(12)
.contentShape(Rectangle()) // << define clickable rect !!
.clipped()
HStack {
Text("Image #1")
.font(.system(size: 18))
.bold()
.kerning(0.5)
Spacer()
}.allowsHitTesting(false) // << disable label area !!
}
.padding(2)
.border(Color(.systemRed), width: 2)
}
}
struct TestClippedButton: View {
var body: some View {
Button(action: { print(">> tapped") }) {
ButtonCard()
}.buttonStyle(PlainButtonStyle())
}
}

Resources