Trying to init CustomButton(title: "Add", icon: .add, status: .enable)
My code is below. I do get the title but enums are not working.
Plus recieving error
Cannot convert value of type 'Image' to expected argument type 'String'
at Image(icon)
import SwiftUI
struct CustomButton: View {
var title: String
var icon: String
var status: Color
var body: some View {
Button(action: {
}) {
Text(title)
.foregroundColor(.white)
.background(Color(.green))
.font(Font.custom("SFCompactDisplay", size: 14))
Image(icon)
.renderingMode(.original)
.foregroundColor(.white)
}
}
enum Icon {
case add
case edit
var image: Image {
switch self {
case .add:
return Image("Add")
case .edit:
return Image("Edit")
}
}
}
enum Status {
case enable
case disable
var color : Color {
switch self {
case .enable:
return Color(.green)
case .disable:
return Color(.gray)
}
}
}
init(title: String, icon: Icon, status: Status) {
self.title = title
self.icon = icon.image
self.status = status.color
}
}
I assume you wanted this
struct CustomButton: View {
var title: String
var icon: Icon
var status: Color
var body: some View {
Button(action: {
}) {
Text(title)
.foregroundColor(.white)
.background(Color(.green))
.font(Font.custom("SFCompactDisplay", size: 14))
icon.image
.renderingMode(.original)
.foregroundColor(.white)
}
}
enum Icon {
case add
case edit
var image: Image {
switch self {
case .add:
return Image("Add")
case .edit:
return Image("Edit")
}
}
}
enum Status {
case enable
case disable
var color : Color {
switch self {
case .enable:
return Color(.green)
case .disable:
return Color(.gray)
}
}
}
init(title: String, icon: Icon, status: Status) {
self.title = title
self.icon = icon
self.status = status.color
}
}
I figured it out. It works now.
struct CustomButton: View {
let title: String
let icon : String
let status: Color
#State private var buttonDisabled = true
var body: some View {
Button(action: {
}) {
ZStack(alignment:.bottom) {
HStack {
Text(title)
.foregroundColor(.white)
.font(Font.custom("SFCompactDisplay-Bold", size: 20))
.bold()
.fontWeight(.bold)
.background(status)
Image(icon)
.renderingMode(.original)
.foregroundColor(.white)
.background(Color(.white))
}
.frame(width: 335, height: 20, alignment: .center)
.padding()
.background(status)
}
.cornerRadius(10)
}
}
enum Icon {
case add
case edit
case none
var image: String {
switch self {
case .add:
return "Add"
case .edit:
return "Edit"
case .none:
return "empty"
}
}
}
enum Status {
case enable
case disable
}
init(title: String, icon: Icon, status: Status) {
self.title = title
self.icon = icon.image
if status == .enable {
self.status = Color(#colorLiteral(red: 0, green: 0.6588235294, blue: 0.5254901961, alpha: 1))
} else {
self.status = Color(#colorLiteral(red: 0.501960814, green: 0.501960814, blue: 0.501960814, alpha: 1))
}
}
}
struct CustomButton_Previews: PreviewProvider {
static var previews: some View {
CustomButton(title: "Odeme Yontemi Ekle", icon: .none, status: .enable)
}
}
Related
I'm having trouble figuring out how to smoothly navigate from my SignInView() to my FirstView(). I have my FirstView() inside of a Navigation Stack, but the transition between the views is very abrupt and devoid of the transition that you normally get with the use of a NavigationLink. How can I get the transition to work?
Much appreciated!
Here is the relevant code...
struct ContentView: View {
#EnvironmentObject var viewModel: AppViewModel
var body: some View {
VStack{
NavigationView {
if viewModel.signedIn {
FirstView()
.transition(.slide)
} else {
//.onAppear method is used for keyboard management (See Misc Functions...)
SignInView()
.onAppear(perform: UIApplication.shared.addTapGestureRecognizer)
.navigationBarHidden(true)
}
}
.onAppear {
viewModel.listen()
}
}
}
}
class AppViewModel: ObservableObject {
private var db = Firestore.firestore()
#Published var userInfo: User?
#Published var signedIn: Bool = false
var handle: AuthStateDidChangeListenerHandle?
let authRef = Auth.auth()
var authHandle : AuthStateDidChangeListenerHandle?
var rootInfoCollection : CollectionReference!
var userIdRef = ""
func fetchUserData(){
db.collection("Users").document("\(userIdRef)").getDocument { document, error in
// Check for error
if error == nil {
// Check that this document exists
if document != nil && document!.exists {
self.userInfo = document.map { (documentSnapshot) -> User in
let data = documentSnapshot.data()
let uid = data?["uid"] as? UUID ?? UUID()
let company = data?["company"] as? String ?? ""
let name = data?["name"] as? String ?? ""
let admin = data?["admin"] as? Bool ?? false
let photo = data?["photo"] as? String ?? ""
return User(uid: uid, company: company, name: name, admin: admin, photo: photo)
}
withAnimation {
self.signedIn = true
}
}
}
}
}
func listen(){
handle = authRef.addStateDidChangeListener({ auth, user in
print(user?.email ?? "No User Found")
if let user = auth.currentUser {
self.userIdRef = user.uid
self.rootInfoCollection = Firestore.firestore().collection("/Users/")
DispatchQueue.main.async {
self.fetchUserData()
}
} else {
self.signedIn = false
}
})
}
func signIn(email: String, password: String){
authRef.signIn(withEmail: email, password: password) { result, error in
guard result != nil, error == nil else {
return
}
}
}
}
struct SignInView: View {
#EnvironmentObject var viewModel: AppViewModel
#State private var username : String = ""
#State private var password : String = ""
#State private var shouldShowLoginAlert: Bool = false
#State var selectedImageArray : [Image] = []
var disableLoginButton : Bool {
return self.username.isEmpty || self.password.isEmpty
}
var body: some View {
VStack{
Image(uiImage: #imageLiteral(resourceName: "awText"))
.resizable()
.frame(width: 180, height: 100)
.padding(.bottom, 50)
TextField("Email", text: $username)
.padding(.leading)
.disableAutocorrection(true)
.autocapitalization(.none)
Rectangle().fill(Color.gray.opacity(0.25)).frame(height: 1, alignment: .center).padding(.bottom)
.padding(.bottom)
.onChange(of: self.username, perform: { value in
if value.count > 10 {
self.username = String(value.prefix(20)) //Max 10 Characters for Username.
}
})
SecureField("Password", text: $password)
.padding(.leading)
.disableAutocorrection(true)
.autocapitalization(.none)
Rectangle().fill(Color.gray.opacity(0.25)).frame(height: 1, alignment: .center)
.onChange(of: self.username, perform: { value in
if value.count > 10 {
self.username = String(value.prefix(10)) //Max 10 Characters for Password.
}
})
//SignIn Button
Button(action: {
viewModel.signIn(email: username, password: password)
}, label: {
Text("Sign In")
.disabled(disableLoginButton)
.frame(width: 300, height: 50)
.background(Color.green)
.clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
.padding()
})
}
Replacing the default NavigationView behavior with your own animations isn't necessarily totally straightforward. I'll lay out one possibility, but another would be to use a real NavigationView transition, but just hide the back button once you're on FirstView.
To do the transition yourself, you'll need one root element to NavigationView, an if clause, a transition(.slide) and withAnimation. Here's a simplified version of your code showing just these elements:
class AppViewModel: ObservableObject {
#Published var signedIn = false
}
struct FirstView : View {
var body: some View {
Text("Signed in")
}
}
struct ContentView: View {
#StateObject var viewModel = AppViewModel()
var body: some View {
NavigationView {
VStack {
if viewModel.signedIn {
FirstView()
.transition(.slide)
} else {
Button("Sign me in") {
withAnimation {
viewModel.signedIn = true
}
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.navigationBarHidden(true)
}
}
}
I'm trying to include some blog posts from my WordPress site into my app. I've tested the API using the individual post Ids for each post and I got it to load the data in my view. However, I'm now trying to fetch an array but it doesn't seem to be fetching the IDs related to the posts to populate the view. What am I doing wrong here?
let content: MarkdownData
#State var beholdarticles: BeholdArticle?
#State private var htmlContent = ""
let articles: [BeholdArticle] = []
private func loadArticle() {
guard let url = URL(string: "https://behold.hagleyparksda.com/wp-json/wp/v2/posts") else {
return
}
URLSession.shared.dataTask(with: url) {data, response, error in
guard let data = data else { return }
if let decodedData = try? JSONDecoder().decode(BeholdArticle.self, from: data){
DispatchQueue.main.async {
self.beholdarticles = decodedData
}
}
}.resume()
}
This is the ForEach loop
var body: some View {
ScrollView (.horizontal) {
ForEach(articles) { item in
ZStack {
if beholdarticles?.thumbnail != nil {
WebImage(url: URL(string: beholdarticles!.thumbnail)!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width:350, height: 450)
} else {
Image("behold_imagecard")
.resizable()
.aspectRatio(contentMode: .fill)
}
HStack {
VStack (alignment: .leading) {
Text(beholdarticles?.title.rendered ?? "Loading...")
.font(.system(size: 30, weight: .bold))
.foregroundColor(Color.white)
.frame(width: 270)
// Text(beholdarticles?.title.rendered ?? "Loading...")
// .font(.system(size: 18, weight: .regular))
// .foregroundColor(Color.white)
// .frame(width: 270)
}
Spacer()
}
VStack {
Spacer()
HStack {
Image(uiImage: #imageLiteral(resourceName: "healthicon"))
Text("Spirituality")
.font(.system(size: 23))
.foregroundColor(Color.white)
Spacer()
}
.background(VisualEffectBlurView(blurStyle: .systemUltraThinMaterial))
}.onAppear{
loadArticle()
}
} .frame(width:350, height: 450)
.shadow(color: Color(#colorLiteral(red: 0.2549019754, green: 0.2745098174, blue: 0.3019607961, alpha: 1)) .opacity(0.2), radius: 20, x: /*#START_MENU_TOKEN#*/0.0/*#END_MENU_TOKEN#*/, y:14)
.cornerRadius(30)
}
}
My data model
struct BeholdArticle: Decodable, Identifiable {
var id: Int
var slug: String
var link: String
var thumbnail: String
var title: BeholdArticleTitle
var content: BeholdArticleContent
enum CodingKeys: String, CodingKey {
case thumbnail = "jetpack_featured_media_url"
case slug, link, title, content
case id = "id"
}
}
struct BeholdArticleTitle: Decodable {
var rendered: String
}
struct BeholdArticleContent: Decodable {
var rendered: String
}
I'm simply trying to populate my loop with the data, but it doesnt seem to be grabbing the ID's from api call. Need some help here
I still have the image URL wrapped in a conditional statement. At current the images wont come through. How do I adjust this to the updated setup?
if beholdarticles?.thumbnail != nil {
WebImage(url: URL(string: item.thumbnail)!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width:350, height: 450)
} else {
Image("behold_imagecard")
.resizable()
.aspectRatio(contentMode: .fill)
}
Here's the updated image code:
ForEach(articles) { article in
ZStack {
if article.thumbnail != nil {
WebImage(url: URL(string: article.thumbnail)!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width:350, height: 450)
} else {
Image("behold_imagecard")
.resizable()
.aspectRatio(contentMode: .fill)
}
If you want to decode the JSON from that URL endpoint, you're going to need to use [BeholdArticle].self instead of BeholdArticle.self, since it's an array of data.
Also, instead of calling loadArticle on each element of the ForEach (which will never get called, since it doesn't have data in it at the beginning), call it at the top level of the view.
Here's a pared down example:
struct ContentView: View {
#State private var articles : [BeholdArticle] = []
var body: some View {
ScrollView {
VStack (alignment: .leading) {
ForEach(articles) { article in
Text(article.title.rendered)
.multilineTextAlignment(.leading)
if let thumbnailURL = URL(string: article.thumbnail) {
WebImage(url: thumbnailURL)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width:350, height: 450)
}
}
}
}
.onAppear {
loadArticles()
}
}
private func loadArticles() {
guard let url = URL(string: "https://behold.hagleyparksda.com/wp-json/wp/v2/posts") else {
return
}
URLSession.shared.dataTask(with: url) {data, response, error in
guard let data = data else { return }
if let decodedData = try? JSONDecoder().decode([BeholdArticle].self, from: data){
DispatchQueue.main.async {
self.articles = decodedData
}
}
}.resume()
}
}
There is a button in the TabBar, this button should open the camera
"Button(action: {...}, label: { ..."
The camera code is written in "CameraView" and "CameraModel" :
struct CameraView: View {
#StateObject var camera = CameraModel()
var body: some View {
ZStack{
// Camera preview...
CameraPreview(camera: camera)
.ignoresSafeArea(.all, edges: .all)
VStack{
if camera.isTaken{
HStack {
Spacer()
Button(action: {}, label: {
Image(systemName: "arrow.triangle.2.circlepath.camera")
.foregroundColor(.black)
.padding()
.background(Color.white)
.clipShape(Circle())
})
.padding(.trailing,10)
}
}
Spacer()
HStack{
if camera.isTaken{
Button(action: {}, label: {
Text("Save")
.foregroundColor(.black)
.fontWeight(.semibold)
.padding(.vertical,10)
.padding(.horizontal,20)
.background(Color.white)
.clipShape(Capsule())
})
.padding(.leading)
Spacer()
}
else{
Button(action: {camera.isTaken.toggle()}, label: {
ZStack{
Circle()
.fill(Color.white)
.frame(width: 65, height: 65)
Circle()
.stroke(Color.white,lineWidth: 2)
.frame(width: 75, height: 75)
}
})
}
}
.frame(height: 75)
}
}
.onAppear(perform: {
camera.Check()
})
}
}
class CameraModel: ObservableObject{
#Published var isTaken = false
#Published var session = AVCaptureSession()
#Published var alert = false
// since were going to read pic data....
#Published var output = AVCapturePhotoOutput()
// preview....
#Published var preview : AVCaptureVideoPreviewLayer!
func Check(){
// first checking camerahas got permission...
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
setUp()
return
// Setting Up Session
case .notDetermined:
// retusting for permission....
AVCaptureDevice.requestAccess(for: .video) { (status) in
if status{
self.setUp()
}
}
case .denied:
self.alert.toggle()
return
default:
return
}
}
func setUp(){
// setting up camera...
do{
// setting configs...
self.session.beginConfiguration()
// change for your own...
let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
let input = try AVCaptureDeviceInput(device: device!)
// checking and adding to session...
if self.session.canAddInput(input){
self.session.addInput(input)
}
// same for output....
if self.session.canAddOutput(self.output){
self.session.addOutput(self.output)
}
self.session.commitConfiguration()
}
catch{
print(error.localizedDescription)
}
}
}
// setting view for preview...
struct CameraPreview: UIViewRepresentable {
#ObservedObject var camera : CameraModel
func makeUIView(context: Context) -> UIView {
let view = UIView(frame: UIScreen.main.bounds)
camera.preview = AVCaptureVideoPreviewLayer(session: camera.session)
camera.preview.frame = view.frame
// Your Own Properties...
camera.preview.videoGravity = .resizeAspectFill
view.layer.addSublayer(camera.preview)
// starting session
camera.session.startRunning()
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
}
}
How to call the camera on click Button in TabBar "Button(action: {}, label: {
)"?
This is just an example of how I did it in the past (after all permissions etc... have been done).
Use your code to do the same.
struct ContentView: View {
#State var image: UIImage?
#State private var showCamera = false
var body: some View {
VStack {
Button(action: { self.showCamera.toggle() }) {
Image(systemName: "camera.circle").resizable().frame(width: 100, height: 100)
}
if image != nil {
Image(uiImage: image!).resizable().frame(width: 200, height: 200)
}
} // this is where it happens
.sheet(isPresented: $showCamera, onDismiss: {self.showCamera = false}) {
CameraViewController(photo: $image)
}
}
}
struct CameraViewController: UIViewControllerRepresentable {
#Environment(\.presentationMode) var presentationMode
#Binding var photo: UIImage?
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
}
func makeUIViewController(context: UIViewControllerRepresentableContext<CameraViewController>) -> UIImagePickerController {
let vc = UIImagePickerController()
vc.sourceType = .camera
vc.delegate = context.coordinator
return vc
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate, AVCapturePhotoCaptureDelegate {
var parent: CameraViewController
var captureSession: AVCaptureSession!
var capturePhotoOutput: AVCapturePhotoOutput!
var theCamera: AVCaptureDevice!
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
let photoQualityPrioritizationMode = AVCapturePhotoOutput.QualityPrioritization.speed
init(_ imagePickerController: CameraViewController) {
self.parent = imagePickerController
}
// called when a picture has been taken
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info:[UIImagePickerController.InfoKey : Any]) {
guard let image = info[.originalImage] as? UIImage else {
print("No image found")
return
}
parent.photo = image // <--- the photo image
parent.presentationMode.wrappedValue.dismiss()
}
}
}
I want to be able to use my custom button in multiple places. Right now it can only Navigate to StartWithPhoneView when tapped. When I use somewhere else I want to Navigate to another view too. I can do it by creating two custom buttons but it is code repetition.
struct CustomButtonView: View {
#State var isTapped: Bool = false
var text = ""
var body: some View {
Button(action: {
print("Create account tapped")
self.isTapped.toggle()
}, label: {
RoundedRectangle(cornerRadius: 5)
.frame(width: UIScreen.main.bounds.width / 1.205, height: 44)
.foregroundColor(.blue)
.overlay(Text(text))
.foregroundColor(.white)
})
.padding(.top,15)
NavigationLink("", destination: StartWithPhoneView(), isActive: $isTapped)
}
}
I am using Custom Button in this SignUpView
struct SignupView: View {
var body: some View {
NavigationView {
VStack {
CustomButtonView(text: "Create an account" )
}
NavigationLink("", destination: StartWithPhoneView(), isActive: CustomButtonView.$isTapped) // I want to reach inside CustomButtonView to fetch isTapped
}
}
}
You can use Generic type which is a View
struct CustomButtonView<Destination: View>: View { //<-here
#State var isTapped: Bool = false
var destination: Destination //<- here
var text = ""
var body: some View {
Button(action: {
print("Create account tapped")
self.isTapped.toggle()
}, label: {
RoundedRectangle(cornerRadius: 5)
.frame(width: UIScreen.main.bounds.width / 1.205, height: 44)
.foregroundColor(.blue)
.overlay(Text(text))
.foregroundColor(.white)
})
.padding(.top,15)
NavigationLink("bbbbb", destination: destination, isActive: $isTapped) //<- here
}
}
i have a bug, if i click on button before the animation before the card flip back. i think for me the best it would be to disable the button for 2 sec, but i made some research and didnt find anything!
struct CardBack: View {
var body: some View {
Image("back_card")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 250)
}
}
struct ContentView: View {
#State var flipped = false
#State private var cardsFront = ["bigCard1", "bigCard2", "bigCard3", "bigCard4", "bigCard5" ]
#State private var cardBack = "back_card"
#State private var disablled = true
var body: some View {
VStack {
Spacer()
ZStack {
Image(flipped ? self.cardsFront.randomElement()! : self.cardBack)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 250)
.rotation3DEffect(Angle(degrees: flipped ? 180 : 0 ), axis: (x: 0, y: 1, z: 0))
}
Spacer()
HStack {
Button(action: {
withAnimation(.spring()) {
self.flipped.toggle()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
withAnimation(.spring()) {
self.flipped.toggle()
}
}
}) {
Image("circle")
.renderingMode(.original)
}
Button(action: {
}) {
Image("plus")
.renderingMode(.original)
}
iOS 13, Swift 5
You can set the button as disabled initially and then enable it using the same sort of logic I used here.
import SwiftUI
struct ContentView: View {
#State var changeColor = false
var body: some View {
TextView(changeColor: $changeColor)
}
}
struct TextView: View {
#Binding var changeColor: Bool
var body: some View {
Text("Hello World")
.foregroundColor(changeColor ? Color.black: Color.red)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.changeColor.toggle()
}
}
}
}
You are almost there, you just need to use the .appear tag in your code to do this.