I wanted to build a Playground which stores my notes for my next examination using either Xcode playgrounds or just Swift Playgrounds. However, I attempted to build a list with an arrow at the side for navigation in Playgrounds, but with the lack of information after searching lots of tutorials, there's no way I could create a navigation list on
Swift Playgrounds. So I switched to Xcode playgrounds. But I could not seem to load the LiveView.
Note: no errors shown. Xcode playgrounds displays "Successful" but nothing is shown.
import SwiftUI
import PlaygroundSupport
//--------------------------------------------------------------//
// Weighted Asesstment III Notes
// Subject: Science
// Written in: English & SwiftUI
struct IntroView: View {
// arrays cus I wanna put this to a list
let expTech = ["Chromatography","Evaporation", "Distillation", "Dissolving","Magnetic Attraction", "Filtration"]
let expUse = ["Chromatography",
"Method: Paper",
"Used for: ",
"Evaporation",
"Method: Heating",
"Used for: Separating salt & water",
"Distillation",
"Method: Heating of liquid mixture with different boiling points",
"eg. Used for: Obtaining pure water from alcohol",
"Dissolving",
"Method",
"Used for",
"Magnetic Attraction",
//I need complete silence lol
//Iron nickel cobalt steel
"Method: Magnets (I,N,C,S)",
"Used for: Separating magnetic objects from non magnets",
"Filtration",
"Method: Filtering/Use of filter paper",
"Used for: Separating mixture of insoluble solid from liquid"]
var body: some View {
// ZStack
ZStack {
// background color
Color.white
// VStack
VStack {
LinearGradient(gradient: Gradient(colors: [.white, .gray, .black]), startPoint: .leading, endPoint: .trailing)
.mask(Text("WA 3 Sci Notes: M5C7")
.font(.system(size: 30, weight: .bold, design: .monospaced)))
.foregroundColor(Color.black)
.offset(x: 10, y: -325)
.padding()
Text("Types of experimental techniques")
.foregroundColor(Color.black)
.offset(x: -100, y: -710)
.font(.system(size: 25, weight: .semibold))
}
ZStack {
Rectangle()
.frame(width: 340, height: 240)
.cornerRadius(20)
.scaledToFill()
.shadow(color: Color.black, radius: 10)
.offset(x: -100, y: -120)
// list
List(expTech, id: \.self) { expTech in Text(expTech) }
.frame(width: 350, height: 250, alignment: .center)
.cornerRadius(25)
.offset(x: -100, y: -120)
}
Text("Uses of Experimental Techniques")
.foregroundColor(Color.blue)
.offset(x: 80, y: 40)
.font(.system(size: 25, weight: .semibold))
ZStack {
VStack {
Rectangle()
.frame(width: 600, height: 250)
.cornerRadius(20)
.scaledToFill()
.shadow(color: Color.black, radius: 5)
.offset(x: 5, y: 530)
}
}
}
}
}
PlaygroundPage.current.setLiveView(IntroView())
The issue could be that your View doesn't render properly. I put your code into an Xcode project (not a storyboard) and it looks like this:
You can even see it rendering weirdly in the playground:
Related
I would like the Image "Astronaut Meditation (Traced)" and the text "Levitate" to be aligned as in the prototype image, which is in the dimensions of an iPhone 13 pro max. How I want it to look
Here is my ContentView code:
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack
{
//Background
Image("Background")
.edgesIgnoringSafeArea([.top])
**//Meditating Astronaut
Image("Astronaut Meditaton (Traced)")
.position(x: 102, y: 106)
//Levitate
Text("Levitate")
.font(.system(size: 34, weight: .semibold, design: .rounded))
.foregroundColor(Color.white)
.position(x: 110, y: 386)**
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I have included an image of how it is previewed on Xcode:
How it looks in XCode
Do not use static value alignment like position or offset because it’s not stable for all devices, instead just wrap your content inside VStack{} then you will achieve your desired design.
ZStack {
//Background
Image("Background")
.edgesIgnoringSafeArea([.top])
VStack { //start wrapping after background color image
//Meditating Astronaut
Image("Astronaut Meditaton (Traced)")
//Levitate
Text("Levitate")
.font(.system(size: 34, weight: .semibold, design: .rounded))
.foregroundColor(Color.white)
}
}
Like #tail said, you should avoid absolute position & offset, since it will vary from device to device. But if you do want to use them for some reason, you can add a alignment parameter to the ZStack and then use offset to get the desired outcome.
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack(alignment: .center) {
//Background
Image("Background")
.ignoresSafeArea(edges: [.top])
//Meditating Astronaut
Image(systemName: "gear")
.resizable()
.frame(width: 150, height: 150)
.offset(y: -150)
//Levitate
Text("Levitate")
.font(.system(size: 34, weight: .semibold, design: .rounded))
.offset(y: -50)
}
}
}
Even better way would be to use GeometryReader and work with relative values to the screen size:
import SwiftUI
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .center) {
//Background
Image("Background")
.ignoresSafeArea(edges: [.top])
//Meditating Astronaut
Image(systemName: "gear")
.resizable()
.frame(width: 150, height: 150)
.position(x: geometry.size.width / 2, y: geometry.size.height / 5)
//Levitate
Text("Levitate")
.font(.system(size: 34, weight: .semibold, design: .rounded))
// Position with offsetting by the size of image.
// Image frame 150 -> half is 75 + 25 for padding.
.position(x: geometry.size.width / 2, y: geometry.size.height / 5 + 100)
}
.frame(width: geometry.size.width, height: geometry.size.height)
}
}
}
With GeometryReader I would use absolute position and calculate the position of image with the relative values and then use the image size in addition to position the text
Use below code to achieve your actual desire design. Wrap your content in VStack and also add frame to background image for bind in your whole device with device width and height.
And also add Spacer() in VStack to occupy remains bottom space(For move view to up side).
ZStack {
//Background
Image("Background")
.resizable()
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height) //Here add frame to background image
.edgesIgnoringSafeArea(.all)
VStack { //Wrap your view in VStack
//Meditating Astronaut
Image("Astronaut Meditaton (Traced)")
.resizable()
.frame(width: 100, height: 100)
.padding(.top,20)
//Levitate
Text("Levitate")
.font(.system(size: 34, weight: .semibold, design: .rounded))
.foregroundColor(Color.white)
Spacer() //Add Spacer to move view to upper side.
}.padding(.top,UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0 + 150) //Add padding with safe area to top side for upper space(suitable for all devices).
}
I am currently learning SwiftUI and I followed this video to create a custom NavigationBar.
Learn how to create a custom navigation bar with a logo in SwiftUI framework and Xcode - Part 2
The buttons doesn't work when I click on it unless I don't use padding() and ignoreSafeArea(), but without using it, the navigationBar would appear on middle of the page. (I also tried use Spacer() but it didn't work)
Is there anyway to fix this issue?
Code for HomePage()
VStack{
NavigationBarView()
.padding(.horizontal, 15)
.padding(.bottom)
.padding(.top, UIApplication.shared.windows.first?.safeAreaInsets.top)
.shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 5)
.ignoresSafeArea(.all, edges: .top)
Code for NavigationBarView()
HStack{
NavigationLink(
destination: PersonalMenuPage()
,label: {
ZStack {
Image("Profile")
.renderingMode(.original)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40, height: 40)
.padding(.horizontal, 10)
Circle()
.fill(Color.red)
.frame(width:14, height: 14, alignment: .center)
.offset(x: 13, y: -10)
}
}
)
}//: hStack
Here is sample output
Sample output
Let's say I have a ZStack with an indeterminate/flexible width. There are four views within this ZStack. How would I distribute the views evenly... as if I were fanning out a deck of cards (with each card overlapping the next), like this:
I can accomplish this effect with .offset(x: ?) if I know the width of the container:
ZStack {
Text("One")
.frame(width: 99, height: 150, alignment: .topLeading)
.background(Color.blue)
.offset(x: -99)
Text("Two")
.frame(width: 99, height: 150, alignment: .topLeading)
.background(Color.red)
.offset(x: -33)
Text("Three")
.frame(width: 99, height: 150, alignment: .topLeading)
.background(Color.orange)
.offset(x: 33)
Text("Four")
.frame(width: 99, height: 150, alignment: .topLeading)
.background(Color.green)
.offset(x: 99)
}
But what if I don't know the width of the container? Or what if it changes? Or what if the number of views within the container changes?
Basically, I just need to distribute views within the ZStack evenly... no matter what the width is. How?
Since you have that many cards, you should use a ForEach. This will make adding/removing cards easier and saves some code.
But what if I don't know the width of the container?
This is where GeometryReader comes in! You can use it to get the width of the container.
I just need to distribute views within the ZStack evenly
Once you get the width from the GeometryReader, you can just divide by the number of elements in the cards array to calculate each card's width.
struct Card {
var name: String
var color: Color
}
struct ContentView: View {
let cards = [
Card(name: "One", color: .blue),
Card(name: "Two", color: .red),
Card(name: "Three", color: .orange),
Card(name: "Four", color: .green)
]
var body: some View {
ZStack {
GeometryReader { proxy in
ForEach(cards.indices, id: \.self) { index in
let card = cards[index]
Text(card.name) /// add a bit of overlap
.frame(width: proxy.size.width / CGFloat(cards.count) + 10, height: 150, alignment: .topLeading)
.background(
card.color
.shadow(color: Color.black, radius: 5) /// make the overlap a bit more visible
)
.offset(x: proxy.size.width / CGFloat(cards.count) * CGFloat(index))
}
.frame(maxHeight: .infinity) /// center the cards vertically
}
}
}
}
Result:
try this:
create a function that returns x spacers (x for the number of text boxes that you want - 1) and inserts a Text view where you want it.
set the width to whatever you want it to be
have the text align to the left
replace all of the Text views in your code with this function
This should get the result you are looking for.
I am trying to clip the image and as we see the UI it looks fine but actually it doesn't clip the image which causes other UI elements to unresponsive.
Here the code I am using.
struct ImageContentView: View {
var urls:[String] = [
"https://lh3.googleusercontent.com/proxy/80im-IBfLODpLDj8d02uEpSVIhqdjen6H6CeFwgRBxeua-Dgw0R3WONFj1Gk8CwB_MufmC9rQ8qHjyWMejwFcJ1PA2s8AAu5WVsmJA=s0-d",
"https://wallpaperaccess.com/full/530919.jpg"
]
var body: some View {
ScrollView{
VStack{
Button(action: {
}, label: {
Text("Hello")
})
VStack(spacing: 20.0) {
ForEach(self.urls, id:\.self) { url in
WebImage(url: URL.init(string: url)!)
.resizable()
.aspectRatio(contentMode: ContentMode.fill)
.frame(height: UIScreen.main.bounds.size.width * 0.5)
.clipped()
.cornerRadius(10.0)
.shadow(color: Color.red, radius: 10.0, x: 0, y: 0)
}
}.padding()
}
}
}
}
Here is fixed part (tested with Xcode 12 / iOS 14)
VStack(spacing: 20.0) {
ForEach(self.urls, id:\.self) { url in
WebImage(url: URL.init(string: url)!)
.resizable()
.aspectRatio(contentMode: ContentMode.fill)
.frame(height: UIScreen.main.bounds.size.width * 0.5)
.clipped()
.cornerRadius(10.0)
.shadow(color: Color.red, radius: 10.0, x: 0, y: 0)
}.contentShape(Rectangle()) // << here !!
}.padding()
Note: I don't know what is your WebImage but with Image and local images it was reproduced as well, so fix was tested.
iOS 13 and +
An other solution is combine compositingGroup and clipped:
That wraps this view in a compositing group and clips.
Note: Respect this order compositingGroup and clipped
Image(..)
.compositingGroup()
.clipped()
I have tried for the above methods but still not working.
Here is another solution for someone who have the same struggle, maybe this can help you :) This line is to disable the tap gesture of the image.
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.contentShape(RoundedRectangle(cornerRadius: 8))
.allowsHitTesting(false) // <- this is the line
I cannot figure out what compositingGroup() is. At first, I thought it is something like Merging layers in Photoshop. But it was not. Because .shadow() effects to the overlay and background views respectively even if I use .compositingGroup().
So far, I've found 2 differences when I use .compositingGroup()
Text doesn't have shadows.
The shadow size of the overlay view is slightly smaller than the above one.
What is the purpose of compositingGroup?
struct ContentView: View {
var body: some View {
VStack(spacing: 50) {
Text("Without\ncompositing")
.font(.largeTitle)
.bold()
.padding()
.foregroundColor(Color.white)
.background(RoundedRectangle(cornerRadius: 30).fill(Color.red))
.padding()
.padding()
.overlay(RoundedRectangle(cornerRadius: 30).stroke(lineWidth: 10))
.shadow(color: .blue, radius: 5)
Text("With\ncompositing")
.font(.largeTitle)
.bold()
.padding()
.foregroundColor(Color.white)
.background(RoundedRectangle(cornerRadius: 30).fill(Color.red))
.padding()
.padding()
.overlay(RoundedRectangle(cornerRadius: 30).stroke(lineWidth: 10))
.compositingGroup() // <--- I added .compositingGroup() here.
.shadow(color: .blue, radius: 5)
}
}
}
This modifier makes the following modifiers be applied to the view as a whole and not to each particular subview separately
Here's an example to better illustrate this:
struct ContentView: View {
let circles: some View = ZStack {
Circle()
.frame(width: 100, height: 100)
.foregroundColor(.red)
.offset(y: -25)
Circle()
.frame(width: 100, height: 100)
.foregroundColor(.blue)
.offset(x: -25, y: 25)
Circle()
.frame(width: 100, height: 100)
.foregroundColor(.green)
.offset(x: 25, y: 25)
}
var body: some View {
VStack(spacing: 100) {
circles
circles
.opacity(0.5)
circles
.compositingGroup()
.opacity(0.5)
}
}
}
So in your case the shadow is applied to the whole view rather than separately to the Text and overlaying RoundedRectangle
Use it when wanting to apply effects like opacity or shadow to a group of views and not each contained element by itself.
It seems like that .shadow() modifier will add both inner and outer shadow. It means that if the view is not "solid", for example, it has a "hole", .shadow() will add shadow like this:
RoundedRectangle(cornerRadius: 30)
.stroke(lineWidth: 10)
.frame(width: 300)
.shadow(color: .blue, radius: 5)
Click to see the image
So, if you do not want the inner shadow, you need to make your view be "solid", like this:
RoundedRectangle(cornerRadius: 30)
.stroke(lineWidth: 10)
.frame(width: 300)
.background(RoundedRectangle(cornerRadius: 30).fill(.white))
.shadow(color: .blue, radius: 5)
Click to see the image
However, something goes wrong again, the inner shadow doesn't disappear.
That's because I forgot to apply the .compositingGroup() modifier.
As #ramzesenok mentioned, .compositingGroup() makes the following modifiers be applied to the view as a whole and not to each particular subview separately.
So, change the code a little bit:
RoundedRectangle(cornerRadius: 30)
.stroke(lineWidth: 10)
.frame(width: 300)
.background(RoundedRectangle(cornerRadius: 30).fill(.white))
.compositingGroup()
.shadow(color: .blue, radius: 5)
Click to see the image
There is only outer shadow now.