I am creating a view that looks like this -
I am struggling to align the summary text with the text inside the image overlay.
I would like the leading edge of Research to align with the leading edge of My experience.
At the moment my view is rendering as
As you can see text view is too close to the leading edge.
I have tried adding .padding() but the view changes to
How can I properly inset the text so these 2 components line up?
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
FeaturedContentCard()
.frame(minHeight: 100)
}.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct FeaturedContentCard: View {
var body: some View {
VStack {
ImageWithTextOverlayView(image: "featured_image", text: "My experience with EAP and Income Protection")
ContentCardSummary(text: "Research shows that our social enviroment significantly impacts our biology")
.padding(.all)
ContentCardMetaView(readTime: "10 min read", name: "Jessica Bell")
}
.padding(.bottom, 16)
.background(Color.white)
.feedItemContainer()
}
}
struct ImageWithTextOverlayView: View {
private(set) var image: String
private(set) var text: String
var body: some View {
Image(image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(minWidth: 0, maxWidth: .infinity)
.overlay(LinearGradient(gradient: Gradient(colors: [Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.6536279966)), Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 0))]), startPoint: .bottom, endPoint: .top))
.overlay(
Text(text)
.foregroundColor(Color.white)
.font(.title)
.padding(),
alignment: .bottomLeading
)
}
}
struct ContentCardSummary: View {
private(set) var text: String
var body: some View {
Text(text)
.frame(alignment: .leading)
.foregroundColor(Color(#colorLiteral(red: 0.2901960784, green: 0.2901960784, blue: 0.2901960784, alpha: 1)))
}
}
struct FeedItemContainer: ViewModifier {
func body(content: Content) -> some View {
content
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
.customShadow()
}
}
struct BenefexShadow: ViewModifier {
func body(content: Content) -> some View {
content
.shadow(color: Color(.init(red: 0.18, green: 0.18, blue: 0.18, alpha: 0.16)).opacity(1), radius: 4, x: 0, y: 1)
.shadow(color: Color(.init(red: 0.18, green: 0.18, blue: 0.18, alpha: 0.1)).opacity(1), radius: 60, x: 0, y: 4)
.shadow(color: Color(.init(red: 0.18, green: 0.18, blue: 0.18, alpha: 0.16)).opacity(1), radius: 4, x: 0, y: 1)
}
}
extension View {
func customShadow() -> some View {
self.modifier(BenefexShadow())
}
func feedItemContainer() -> some View {
self.modifier(FeedItemContainer())
}
}
struct AvatarView: View {
var body: some View {
Image("profile_pic")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 40, height: 40)
.clipShape(Circle())
.overlay(
Circle()
.stroke(Color.white, lineWidth: 2)
)
.customShadow()
}
}
struct ContentCardMetaView: View {
private(set) var readTime: String
private(set) var name: String
var body: some View {
HStack(spacing: 8) {
AvatarView()
VStack(alignment: .leading) {
Text(readTime)
.font(.subheadline)
.foregroundColor(Color(#colorLiteral(red: 0.2901960784, green: 0.2901960784, blue: 0.2901960784, alpha: 1)))
Text(name)
.fontWeight(.medium)
.foregroundColor(Color(#colorLiteral(red: 0.2980392157, green: 0.1843137255, blue: 0.737254902, alpha: 1)))
}
Spacer()
Button(action: { }) {
Image(systemName: "ellipsis")
.font(.system(size: 24))
.foregroundColor(Color(#colorLiteral(red: 0.7803921569, green: 0.7803921569, blue: 0.7803921569, alpha: 1)))
.rotationEffect(.degrees(-90))
}
}
}
}
Here is fix. Tested with Xcode 11.4 / iOS 13.4
1) Remove padding at top layer, so
ImageWithTextOverlayView(image: "large_image", text: "My experience with EAP and Income Protection")
ContentCardSummary(text: "Research shows that our social enviroment significantly impacts our biology")
ContentCardMetaView(readTime: "10 min read", name: "Jessica Bell")
2) Correct alignment of summary, as
struct ContentCardSummary: View {
private(set) var text: String
var body: some View {
Text(text)
.frame(maxWidth: .infinity, alignment: .leading) // << full width
.padding() // << same padding as above text
.foregroundColor(Color(#colorLiteral(red: 0.2901960784, green: 0.2901960784, blue: 0.2901960784, alpha: 1)))
}
}
Related
I got a NavigationBar at the Bottom of some of my Views, but not all of them. It looks like this:
And I need it to refresh in every View once I change to another View since I want to implement changing the Button colors to represent which View is active at the moment.
So if the current View is Home I want it to look like this:
This is the code for the buttonbar:
struct ButtonBar: View {
#State var selection: Int? = nil
var body: some View {
HStack {
NavigationLink(destination: ShopView(), tag: 1, selection: $selection) {
Button() {
self.selection = 1
} label: {
Image("shoppingCart").resizable()
.renderingMode(.template)
.foregroundColor(selection == 1 ? Color.blue : Color(.init(red: 0.59, green: 0.62, blue: 0.67, alpha: 1)))
.frame(width: 34, height: 34)
}
}
Spacer()
NavigationLink(destination: HomeView(), tag: 2, selection: $selection) {
Button() {
self.selection = 2
} label: {
Image("home").resizable()
.renderingMode(.template)
.foregroundColor(selection == 2 ? Color.blue : Color(.init(red: 0.59, green: 0.62, blue: 0.67, alpha: 1)))
.frame(width: 34, height: 34)
}
}
Spacer()
NavigationLink(destination: MessagesView(), tag: 3, selection: $selection) {
Button() {
self.selection = 3
} label: {
Image("message").resizable()
.renderingMode(.template)
.foregroundColor(Color(.init(red: 0.59, green: 0.62, blue: 0.67, alpha: 1)))
.frame(width: 24, height: 24)
}
}
Spacer()
NavigationLink(destination: EventsView(), tag: 4, selection: $selection) {
Button() {
self.selection = 4
} label: {
Image("calender").resizable()
.renderingMode(.template)
.foregroundColor(Color(.init(red: 0.59, green: 0.62, blue: 0.67, alpha: 1)))
.frame(width: 34, height: 34)
}
}
Spacer()
NavigationLink(destination: SettingsView(), tag: 5, selection: $selection) {
Button() {
self.selection = 5
} label: {
Image("personCover").resizable()
.renderingMode(.template)
.foregroundColor(Color(.init(red: 0.59, green: 0.62, blue: 0.67, alpha: 1)))
.frame(width: 36, height: 36)
}
}
}.padding(.init(top: 0, leading: 8, bottom: 8, trailing: 8))
}
}
When I render this View in my Views like this ButtonBar() it does not refresh colors since it is created as a new View in every View. Is there a way to pass a View as an environmentObject just like with States?
Create your TabView in ContentView. There'll be three tab items: one that will lead the user to HomeView, one that will lead the user to ShopView, and another one to MessageView.Make sure to have a Navigation Link in your parent View that will lead to a child View. For example, in the code below:
import SwiftUI
struct ContentView: View {
#State private var selection = 0
var body: some View {
NavigationView {
TabView(selection: $selection){
NavigationLink(destination: HomeView()){
Text("Home Tab")
.font(.system(size: 30, weight: .bold, design: .rounded))
}
.tabItem {
Image(systemName: "house.fill")
Text("Home")
}
.tag(0)
NavigationLink(destination: ShopView()){
Text("Shop Tab")
.font(.system(size: 30, weight: .bold, design: .rounded))
}
.tabItem {
Image(systemName: "bookmark.circle.fill")
Text("Shop")
}
.tag(1)
NavigationLink(destination: MessageView()){
Text("Message Tab")
.font(.system(size: 30, weight: .bold, design: .rounded))
}
.tabItem {
Image(systemName: "message.fill")
Text("Message")
}
.tag(2)
}
.accentColor(.yellow)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Output :
Using TabView would work perfectly if I didn't need custom buttons, but this code worked for me:
import Foundation
class ViewRouter: ObservableObject {
#Published var currentPage: Page = .home
}
enum Page {
case home
case shop
case messages
case events
case settings
}
struct ContentView: View {
#StateObject var viewRouter = ViewRouter()
var body: some View {
VStack {
ZStack {
switch viewRouter.currentPage {
case .shop:
ShopView().environmentObject(loginViewController)
case .home:
HomeView()
case .messages:
MessagesView()
case .events:
EventsView()
case .settings:
SettingsView()
}
}
HStack {
Button() {
viewRouter.currentPage = .shop
} label: {
Image("shoppingCart").resizable()
.renderingMode(.template)
.foregroundColor(viewRouter.currentPage == .shop ? Color.blue : Color(.init(red: 0.59, green: 0.62, blue: 0.67, alpha: 1)))
.frame(width: 34, height: 34)
}
Spacer()
Button() {
viewRouter.currentPage = .home
} label: {
Image("home").resizable()
.renderingMode(.template)
.foregroundColor(viewRouter.currentPage == .home ? Color.blue : Color(.init(red: 0.59, green: 0.62, blue: 0.67, alpha: 1)))
.frame(width: 34, height: 34)
}
Spacer()
Button() {
viewRouter.currentPage = .messages
} label: {
Image("message").resizable()
.renderingMode(.template)
.foregroundColor(viewRouter.currentPage == .messages ? Color.blue : Color(.init(red: 0.59, green: 0.62, blue: 0.67, alpha: 1)))
.frame(width: 24, height: 24)
}
Spacer()
Button() {
viewRouter.currentPage = .events
} label: {
Image("calender").resizable()
.renderingMode(.template)
.foregroundColor(viewRouter.currentPage == .events ? Color.blue : Color(.init(red: 0.59, green: 0.62, blue: 0.67, alpha: 1)))
.frame(width: 34, height: 34)
}
Spacer()
Button() {
viewRouter.currentPage = .settings
} label: {
Image("personCover").resizable()
.renderingMode(.template)
.foregroundColor(viewRouter.currentPage == .settings ? Color.blue : Color(.init(red: 0.59, green: 0.62, blue: 0.67, alpha: 1)))
.frame(width: 36, height: 36)
}
}.padding(.init(top: 0, leading: 8, bottom: 8, trailing: 8))
}
}
}
}
For some reason I can't see the back button in the Navigation View.
I don't know if there is some extra code that I need to add in the child view but I don't know. I more or less copied the code from the tutorial, but I can't see the back button in the child view.
var body: some View {
VStack {
//Rectangle 12
Rectangle()
.fill(Color(#colorLiteral(red: 0.7137255072593689, green: 0.10196077823638916, blue: 0.10196077823638916, alpha: 1)))
.frame(width: .infinity, height: 50)
.padding(.bottom, 0.0)
NavigationView {
ZStack{
Color(red: 0.9, green: 0.9, blue: 0.9).edgesIgnoringSafeArea(.all)
ScrollView(.vertical, showsIndicators: true) {
HStack{
Text("Marketplace").font(.custom("Arial", size: 35))
.fontWeight(.medium)
.foregroundColor(Color(red: 0.1, green: 0.1, blue: 0.1))
.padding(EdgeInsets(top: 15, leading: 20, bottom: -2, trailing: 0))
Spacer()
}
ForEach(produitsdfsdf, id: \.id ) { product in
NavigationLink(destination: TempView()) {
LandingPresets.listItem(name: product.name, description: product.price, rate: product.category)
}
}
}
.navigationBarHidden(true)
}
}
.padding(.top, -8.0)
}.ignoresSafeArea(edges: .top)
.onAppear {
ProductAPI().getAllProducts {(productArr) in
self.produitsdfsdf = productArr
}
}
}
It is because of you use .navigationBarHidden(true) in main View.
You could add .navigationBarTitleDisplayMode(.inline) to your TempView().
If you give code of TempView, I'll be able to show to which part of code.
I have been trying to get this debossed effect in SwiftUI but nothing is working out. I tried shadow(color:radius:x:y:) modifier but it applies shadow on the outside of symbol. Can you please tell me if there are any other APIs or tricks to achieve this ?
Thank You !
Love the debossed effect, reminds me of iOS 6. The key was finding inverseMask and then I had a play around and came up with this:
import SwiftUI
extension View {
// https://www.raywenderlich.com/7589178-how-to-create-a-neumorphic-design-with-swiftui
func inverseMask<Mask>(_ mask: Mask) -> some View where Mask: View {
self.mask(mask
.foregroundColor(.black)
.background(Color.white)
.compositingGroup()
.luminanceToAlpha()
)
}
}
struct DebossTest: View {
static var lightPurple = UIColor(red: 212/255, green: 206/255, blue: 247/255, alpha: 1)
var body: some View {
ZStack {
Color(uiColor: DebossTest.lightPurple)
MyButton()
.font(.system(size: 144))
}
}
}
struct MyButton: View {
static var darkPurple = UIColor(red: 140/255, green: 134/255, blue: 211/255, alpha: 1)
let trashName = "trash.fill"
var body: some View {
ZStack {
// the darker inset image
Image(systemName: trashName)
.foregroundColor(Color(uiColor: MyButton.darkPurple))
// black inner shadow
Rectangle()
.inverseMask(Image(systemName: trashName))
.shadow(color: Color.black, radius: 1, x: 0, y: 1)
.mask(Image(systemName: trashName))
.clipped()
// white bottom edges
Image(systemName: trashName)
.shadow(color: Color.white, radius: 1, x: 0, y: 1)
.inverseMask(Image(systemName: trashName))
}
.frame(width: 185, height: 140)
}
}
struct DebossTest_Previews: PreviewProvider {
static var previews: some View {
DebossTest()
}
}
The answer of malhal is perfect, but my approach is deferent than his approach. My codes can get updated for clipping view via an Image which normally we can do with Shape, but that is for another day, here is my way:
struct ContentView: View {
var body: some View {
Color.yellow
.overlay(
Circle()
.fill(Color.black)
.frame(width: 100, height: 100)
.opacity(0.1)
.overlay(
ZStack {
Circle()
.stroke(Color.black, lineWidth: 3)
.blur(radius: 5)
ZStack {
Image(systemName: "info")
.resizable()
.scaledToFit()
.foregroundColor(Color.black)
.blur(radius: 5)
.opacity(0.5)
Image(systemName: "info")
.resizable()
.scaledToFit()
.foregroundColor(Color.yellow)
}
.padding()
}
)
.clipShape(Circle())
)
}
}
I am currently developing an app, and I am trying to setup the Views to be accurate for different screen sizes. I get the design to what I would like in the preview, but then when I run it in the simulator some things are a little off. To note not all screens do this, just a few. Any reason why this may be?
Screenshot for reference: Xcode/Simulator Screenshot
ContentView:
struct ContentView: View {
//Removed variables for checking log ins and loading screen animation
var body: some View {
VStack{
//Check if logged in and not first time
if status && !firstTime{
ZStack{
NavigationView{
TabView(){
Tab1View()
.environmentObject(ContentViewModel())
Tab2View()
.environmentObject(ContentViewModel())
}
.navigationBarHidden(true)
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
ZStack{
//LoadingScreen
}
.ignoresSafeArea(edges: .all)
.onAppear(perform: animateSplash)
.opacity(endSplash ? 0 : 1)
}
}
//Check if logged and first time
else if status && firstTime{
ZStack{
NavigationView{
if role == "owner"{
WelcomeView()
} else {
WelcomeEmployeeView()
}
}
}
} else {
NavigationView{
//View in the screenshot
CreateView()
}
}
}
CreateView:
import SwiftUI
import FirebaseAuth
struct CreateView: View {
#EnvironmentObject var model: ContentViewModel
#State var companyName = ""
#State var employeeName = ""
#State var phoneNumber = ""
#State var show = false
#State var message = ""
#State var alert = false
#State var ID = ""
#State var test = ""
#State var role = "owner"
var body: some View {
ZStack{
ZStack(alignment: .topTrailing){
Path{ path in
path.move(to: CGPoint(x: 500, y: 0))
path.addLine(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 0, y: 80))
path.addLine(to: CGPoint(x:500, y: 180))
}
.fill(LinearGradient(gradient:Gradient(colors: [Color(#colorLiteral(red: 0.6470588235, green: 0.8196078431, blue: 0.7450980392, alpha: 1)), Color(#colorLiteral(red: 0.2549019608, green: 0.7607843137, blue: 0.8588235294, alpha: 1)), .white, .green]), startPoint: .topLeading, endPoint: .bottomTrailing))
.edgesIgnoringSafeArea(.top)
Image("icon-white")
.resizable()
.frame(width: 50, height: 60)
.padding(.top)
.padding(.trailing, 30)
}
VStack(alignment: .leading){
Text("Create a")
.foregroundColor(Color(#colorLiteral(red: 0.2549019608, green: 0.7607843137, blue: 0.8588235294, alpha: 1)))
.font(.custom("Poppins-Medium", size: 30))
Text("Company Account")
.foregroundColor(Color(#colorLiteral(red: 0.2549019608, green: 0.7607843137, blue: 0.8588235294, alpha: 1)))
.font(.custom("Poppins-Medium", size: 30))
.padding(.bottom, 30)
Text("Company Name")
.foregroundColor(Color(#colorLiteral(red: 0.2549019608, green: 0.7607843137, blue: 0.8588235294, alpha: 1)))
.font(.custom("Poppins-Semibold", size: 16))
TextField("", text: $companyName)
.frame(width: 290)
.padding(14)
.overlay(RoundedRectangle(cornerRadius: 5.0).strokeBorder(Color.black, style: StrokeStyle(lineWidth: 0.5)))
.padding(.bottom)
Text("Employee Name")
.foregroundColor(Color(#colorLiteral(red: 0.2549019608, green: 0.7607843137, blue: 0.8588235294, alpha: 1)))
.font(.custom("Poppins-Semibold", size: 16))
TextField("", text: $employeeName)
.frame(width: 290)
.padding(14)
.overlay(RoundedRectangle(cornerRadius: 5.0).strokeBorder(Color.black, style: StrokeStyle(lineWidth: 0.5)))
.padding(.bottom)
Text("Phone Number")
.foregroundColor(Color(#colorLiteral(red: 0.2549019608, green: 0.7607843137, blue: 0.8588235294, alpha: 1)))
.font(.custom("Poppins-Semibold", size: 16))
TextField("", text: $phoneNumber)
.frame(width: 290)
.padding(14)
.overlay(RoundedRectangle(cornerRadius: 5.0).strokeBorder(Color.black, style: StrokeStyle(lineWidth: 0.5)))
.padding(.bottom)
VStack(alignment: .center){
NavigationLink(
destination: VerifcationView(show: $show, ID: $ID, name: $employeeName, companyName: $companyName, phoneNumber: $phoneNumber, hasCode: $test, role: $role), isActive: $show){
ZStack{
RoundedRectangle(cornerRadius: 5)
.foregroundColor(Color(#colorLiteral(red: 0.2549019608, green: 0.7607843137, blue: 0.8588235294, alpha: 1)))
.frame(width: 315,height: 50)
Button(action: {
PhoneAuthProvider.provider().verifyPhoneNumber( "+"+self.phoneNumber, uiDelegate:nil){ (ID, error) in
if error != nil {
self.message = (error?.localizedDescription)!
self.alert.toggle()
return
}
self.ID = ID!
self.show.toggle()
}
}, label: {
Text("Create Account")
.font(.custom("Poppins-Medium", size: 18))
.foregroundColor(.white)
})
}
}
NavigationLink(
destination: JoinView()){
Text("Join as Employee")
.foregroundColor(Color(red: 141/255, green: 141/255, blue: 141/255))
.underline(/*#START_MENU_TOKEN#*/true/*#END_MENU_TOKEN#*/, color: Color(red: 141/255, green: 141/255, blue: 141/255))
}
}
.padding(.top)
}
.padding(.top)
}
.navigationBarBackButtonHidden(true)
}
}
For SwiftUI previews, you just preview a single view - CreateView. This is the only view that gets previewed. Meanwhile, your simulator runs the entire app, starting all the way back from your ContentView.
Note that inside ContentView, you're wrapping CreateView in a NavigationView.
NavigationView{
// View in the screenshot
CreateView()
}
This will add a gap at the top of the screen, pushing everything else down (resulting in the difference that you see). Even the icon-white image is pushed down into the white part of the screen, which is why you can't see it in the simulator.
I write the following code and display my days array into Hstack scrollview.
I want to scroll with button actions for that using scrollProxy by using ScrollViewReader
I implemented as follows
when I make a left and right button actions it works as expected
But when I come into screen initially shows the last index but it's not working for me
Anyone gives an idea what the correct place to move the last index.
var body: some View {
ScrollViewReader { scrollProxy in
ZStack{
VStack {
Button(action:{
scrollProxy.scrollTo(selectedDay) //<---- Working
})
.padding(.leading, 10)
Spacer()
ScrollView(.horizontal, showsIndicators: false){
HStack(spacing: 20) {
ForEach(days.indices, id: \.self) { i in
CalendarView(
number: self.days[i].number,
days: self.days[i].weekday,
color: self.days[i].isToday ? #colorLiteral(red: 0.9060331583, green: 0.2547450066, blue: 0.3359550834, alpha: 1) : #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1),
textcolor: self.days[i].isToday ? #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) : #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
)
.onTapGesture{
print(self.days[i])
// this is just for replacing the current selection
for j in self.days.indices { self.days[j].isToday = false }
self.days[i].isToday = true
}
}}
.padding(.leading,10)
.padding(.bottom, 10)
.shadow(radius: 3, x: 3, y: 3)
}
Spacer()
Button(action:{
scrollProxy.scrollTo(selectedDay) //<-- Working
})
.padding(.leading)
Spacer()
}
}
.onAppear {
self.getCurrentWeekdays()
scrollProxy.scrollTo(days.count - 1) //<-- Here it is not Work.
}
}
}