Live Activities is not working with custom colours - swiftui

When I try to use custom colours in Live Activities, the activity in the Lock Screen is not appear at all.
In the code, I use an extension to conform Color to Codable, otherwise is not compile. (I found the Color extension here)
struct MeetingTimerAttributes: ActivityAttributes {
public typealias TimeTrackingStatus = ContentState
public struct ContentState: Codable, Hashable {
var elapsedTime: Int
var remainingTime: Int
var meetingDuration: Double
var mainColor: Color
}
}
extension Color: Codable {
// Full code in the link
}
And here is the Live Activity view:
struct TimeTrackingWidgetView: View {
let context: ActivityViewContext<MeetingTimerAttributes>
var body: some View {
VStack {
Text("Total Time: \(Int(context.state.meetingDuration)) minutes")
.padding(.top, 10)
.bold()
HStack {
VStack(alignment: .leading) {
Text("Minutes Elapsed")
.font(.caption)
Label("\(context.state.elapsedTime.secondsToMinutes())", systemImage: "hourglass.bottomhalf.fill")
}
Spacer()
VStack(alignment: .trailing) {
Text("Minutes Remaining")
.font(.caption)
Label("\(context.state.remainingTime.secondsToMinutes())", systemImage: "hourglass.tophalf.fill")
.labelStyle(.titleAndIcon)
}
}
.padding(.init(top: 5, leading: 15, bottom: 10, trailing: 15))
}
.background(context.state.mainColor) <---- This line
}
}
If I use .background(context.state.mainColor) the Live activity is not appear but if I use background(.red), for example, it works just fine.
The color are sRGB created in the assets catalog, like this one:
Any idea?

Related

Passing data across views for unique objects in a forEach loop in swift

I have two views, ViewAssignment and TaskDetailView. My ViewAssignment page fetches data from an environment object, and creates a list using the data.
Upon each item of the list being clicked on, the TaskDetailView pops in as a navigation link, however, I am having trouble making the information in the TaskDetailView unique to that particular iteration (the item in the list)
I believe the trouble comes from my TaskDetailView.swift
import SwiftUI
struct TaskDetailView: View {
#EnvironmentObject var assignment: Assignments
#State var taskNotes = ""
var body: some View {
VStack(spacing: 10) {
Image("english-essay")
.resizable()
.scaledToFit()
.frame(width: 250, height: 160)
.cornerRadius(20)
Text(self.assignment.data.first?.taskName ?? "Untitled Task")
.font(.title2)
.fontWeight(.semibold)
.lineLimit(2)
.multilineTextAlignment(.center)
HStack(spacing: 20) {
Label(self.assignment.data.first?.weighting ?? "0", systemImage: "percent")
.font(.subheadline)
.foregroundColor(.secondary)
Text(self.assignment.data.first?.dueDate ?? "No Date")
.font(.subheadline)
.foregroundColor(.secondary)
}
TextField("Write any notes here", text: $taskNotes)
.font(.body)
.padding()
Spacer()
}
}
}
struct TaskDetailView_Previews: PreviewProvider {
static var previews: some View {
TaskDetailView() // I assume there is some information I have to pass through here
}
}
For details, this is my other view:
import SwiftUI
struct ViewAssignment: View {
// Observed to update the UI
#EnvironmentObject var assignment: Assignments
var body: some View {
ZStack {
NavigationView {
List(self.assignment.data) { task in
NavigationLink (
destination: TaskDetailView(),
label: {
Image(systemName: "doc.append.fill")
.scaleEffect(2.5)
.padding()
VStack(alignment: .leading, spacing: 3) {
Text(task.taskName)
.fontWeight(.semibold)
.lineLimit(2)
Text(task.dueDate + " - " + task.subject)
.font(.subheadline)
.foregroundColor(.secondary)
}
})
}
.navigationTitle("My Tasks")
.listStyle(InsetGroupedListStyle())
}
}
}
}
struct ViewAssignment_Previews: PreviewProvider {
static var previews: some View {
ViewAssignment()
}
}
I would also like to know if, upon making the screen unique for each item in the list, would I be able to have the contents of the text field saved upon reloading the app, Perhaps through #AppStorage?
Thank you for the assistance.
If I understand correctly what you are trying to do:
a TaskDetailView displays the detail of a ... Task.
So you should have a Task structure like this:
struct Task {
let name: String
let subject: String
...
}
You have to create one (or more) instance of Task to test your TaskDetailView:
extension Task {
var test: Task {
Task(name: "Test", subject: "Test Subject")
}
}
Now in the preview of your TaskDetailView you can try to display your example :
struct TaskDetailView_Previews: PreviewProvider {
static var previews: some View {
TaskDetailView(task: Task.test) // here
}
}
For the moment nothing is happening. Because your TaskDetailView doesn't have a task parameter.
struct TaskDetailView: View {
var task: Task
var body: some View {
...
}
Now its body can use the different parameters of this Task.
Text(task.name)
.font(.title2)
.fontWeight(.semibold)
.lineLimit(2)
.multilineTextAlignment(.center)
Now in your List:
List(self.assignment.data) { task in
NavigationLink (
destination: TaskDetailView(task: task), // <- here !!!
label: {
Image(systemName: "doc.append.fill")
.scaleEffect(2.5)
.padding()
VStack(alignment: .leading, spacing: 3) {
Text(task.name)
.fontWeight(.semibold)
.lineLimit(2)
}
})
}

Why do the views extend wider than the screen?

Edit: Substitute your "system name:" of choice. "pencil.circle" works fine. "edit" is not a valid SF Symbol.
(I've simplified my code so you can cut and paste. That's why you see .frame, resizable, etc. where much simpler code might your first instinct.)
I have created a view which is a vertical list of row items (table view).
Each row item has a horizontal view with two images inside it.
The images take up too much space and do not fit correctly on the screen:
import SwiftUI
#main
struct StackOverflowDemoApp: App {
var body: some Scene {
WindowGroup {
TandemView()
}
}
}
struct PaddedImageView: View {
let color: Color = .red
var body: some View {
ZStack {
color
Image(systemName: "edit")
.resizable()
.padding()
}
Spacer()
}
}
struct TandemView: View {
var body: some View {
HStack {
Spacer()
Image(systemName: "pencil")
.resizable()
.background(Color.orange)
.frame(height: 80)
.aspectRatio(1, contentMode: .fill)
PaddedImageView()
.frame(width: 200, height: 80)
}
.padding()
.fixedSize()
}
}
struct TandemView_Previews: PreviewProvider {
static var previews: some View {
TandemView()
}
}
The above is the closest I can get to the desired layout (it just needs to fit horizontally). I experimented with GeometryReader but that did not produce desired results.
Here are some things I tried:
The code as provided
NoConstraintsOnPencilOrHStack
NoConstraintsOnTandemView
NoConstraintsOnImageInPaddedViewButWithFrameConstraint
I am trying to get a row view which consists of two Images (my actual source consists of UIImage objects) that fits within the width of the screen.
Edit:
After Accepting cedricbahirwe's spot-on response, I was able to simplify the code further. New results:
I added at the top level
TandemView()
.padding(.horizontal)
I removed:
// Spacer()
at the end of PaddedImageView
updated TandemView -- changed both frames and removed 3 lines:
struct TandemView: View {
var body: some View {
HStack {
Spacer()
Image(systemName: "pencil")
.resizable()
.background(Color.orange)
.frame(width: 80, height: 80)
// .aspectRatio(1, contentMode: .fill)
PaddedImageView()
.frame(height: 80)
}
// .padding()
// .fixedSize()
}
}
This is happening because of the layout of PaddedImageView View, you can actually remove the Spacer since it is not needed there.
So change
struct PaddedImageView: View {
let color: Color = .red
var body: some View {
ZStack {
color
Image(systemName: "edit")
.resizable()
.padding()
}
Spacer()
}
}
to
struct PaddedImageView: View {
let color: Color = .red
var body: some View {
ZStack {
color
Image(systemName: "edit")
.resizable()
.padding()
}
}
}
Note:
SwiftUI Engine infers the layout of your view from the implementation of the body property. It's recommended to have one Parent View inside the body property.

How to layout properly in ZStack (I have visibility problem)?

Here is reproducable small code below;
As you'll see when you run the demo code, the Element view does stay under Color.blue when dragged eventhough its above according to ZStack. By the way I also played with zIndex modifier but still no luck. Any solution you offer? Thanks all.
struct ContentView: View {
var body: some View {
GeometryReader { gr in
ZStack {
Color.blue.opacity(0.3)
.aspectRatio(1, contentMode: .fit)
.frame(width: gr.size.width)
VStack {
Spacer()
ScrollView(.horizontal) {
HStack {
ForEach(1...15, id: \.self) { (idx) in
Element(index: idx)
}
}
.padding()
}
.background(Color.secondary.opacity(0.3))
}
}
}
}
}
struct Element: View {
#State private var dragAmount = CGSize.zero
var index: Int
var body: some View {
Rectangle()
.frame(width: 80, height: 80)
.overlay(Text("\(index)").bold().foregroundColor(.white))
.offset(dragAmount)
.gesture(
DragGesture(coordinateSpace: .global)
.onChanged {
self.dragAmount = CGSize(width: $0.translation.width, height: $0.translation.height)
}
.onEnded { _ in
self.dragAmount = .zero
}
)
}
}
iOS 15.5: still valid
How can achieve my goal then, like dragging Element on different view (in this scenario Color.blue)
Actually we need to disable clipping by ScrollView.
Below is possible approach based on helper extensions from my other answers (https://stackoverflow.com/a/63322713/12299030 and https://stackoverflow.com/a/60855853/12299030)
VStack {
Spacer()
ScrollView(.horizontal) {
HStack {
ForEach(1...15, id: \.self) { (idx) in
Element(index: idx)
}
}
.padding()
.background(ScrollViewConfigurator {
$0?.clipsToBounds = false // << here !!
})
}
.background(Color.secondary.opacity(0.3))
}

Edited: How we can alignment Image with Images and Text withTexts in SwiftUI, Multi-alignment?

I have a VStack which has some HStack as you can see in my codes, inside my each Hstack there is an Image and Text, after running my codes the Alignmet of all codes is ugly, I want the Image alignment center together and Text alignment leading. How I can solve the problem?
I can make all Image .center Alignment, and also all Text .leading Alignment. But I can not make both happen at same time.
struct CustomAlignment: AlignmentID
{
static func defaultValue(in context: ViewDimensions) -> CGFloat
{
return context[HorizontalAlignment.center]
}
}
struct CustomAlignment2: AlignmentID
{
static func defaultValue(in context: ViewDimensions) -> CGFloat
{
return context[HorizontalAlignment.leading]
}
}
extension HorizontalAlignment
{
static let custom: HorizontalAlignment = HorizontalAlignment(CustomAlignment.self)
static let custom2: HorizontalAlignment = HorizontalAlignment(CustomAlignment2.self)
}
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(alignment: .custom)
{
HStack()
{
Image(systemName: "folder")
.alignmentGuide(.custom) { $0[HorizontalAlignment.center] }
Text("Some texts here.")
.alignmentGuide(.custom2) { $0[HorizontalAlignment.leading] }
Spacer()
}
HStack()
{
Image(systemName: "music.note")
.alignmentGuide(.custom) { $0[HorizontalAlignment.center] }
Text("Some texts here.")
.alignmentGuide(.custom2) { $0[HorizontalAlignment.leading] }
Spacer()
}
HStack()
{
Image(systemName: "person.fill.questionmark")
.alignmentGuide(.custom) { $0[HorizontalAlignment.center] }
Text("Some texts here.")
.alignmentGuide(.custom2) { $0[HorizontalAlignment.leading] }
Spacer()
}
}
.padding()
Spacer()
}
}
Use custom alignment guide if you want precise control.
About your comment on using fixed frame, here is an article which explains how frame works in SwiftUI.
Basically, frame modifier just adds a fixed size frame around the SF in this case, but it won't alter the intrinsic size.
struct ContentView: View {
var body: some View {
VStack(alignment: .sfView) {
SFView(title: "This is some text", image: "folder")
SFView(title: "SwiftUI is cool. Combine is cooler.", image: "snow")
SFView(title: "This is a music note. This has a different length.", image: "music.note")
}
}
}
private struct SFView: View {
let title: String
let image: String
var body: some View {
HStack(spacing: 8) {
Image(systemName: image)
.font(.system(size: 20))
.frame(width: 32, height: 32)
.alignmentGuide(.sfView) { d in d[HorizontalAlignment.center] }
Text(title)
.alignmentGuide(.sfView) { d in d[HorizontalAlignment.leading] }
}
}
}
private extension HorizontalAlignment {
struct SFViewAlignment: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
d[HorizontalAlignment.leading]
}
}
static let sfView = HorizontalAlignment(SFViewAlignment.self)
}
You have to give a frame to the image as some SF Symbols are larger than others, also try to create reusable views.
try something like this:
struct ContentView: View {
var body: some View {
VStack {
RowView(title: "Some texts here.", image: "folder")
RowView(title: "Some texts here.", image: "person.fill.questionmark")
RowView(title: "Some texts here.", image: "snow")
RowView(title: "Some texts here.", image: "forward.end.alt.fill")
}
.padding()
Spacer()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct RowView: View {
let title: String
let image: String
var body: some View {
// Option 1 with Label
// Label(
// title: {
// Text(title)
// },
// icon: {
// Image(systemName: image)
// .frame(width: 30, height: 30)
// }
// )
// Option 2 with HStack
HStack {
Image(systemName: image)
.frame(width: 30, height: 30)
Text(title)
Spacer()
}
}
}
You are WAY overcomplicating it. You don't need to use all these custom alignments and constraints for something this simple.
Go back to basics and use a regular VStack / HStack and just set the icon to be an exact frame. The issue was arising because the icons had slightly different widths.
struct TestView: View {
var body: some View {
VStack(alignment: .leading) {
HStack {
Image(systemName: "folder")
.frame(width: 30, height: 30, alignment: .center)
Text("Some text here")
}
HStack {
Image(systemName: "person.fill.questionmark")
.frame(width: 30, height: 30, alignment: .center)
Text("Some text here")
}
HStack {
Image(systemName: "snow")
.frame(width: 30, height: 30, alignment: .center)
Text("Some text here")
}
HStack {
Image(systemName: "music.note")
.frame(width: 30, height: 30, alignment: .center)
Text("Some text here")
}
}
}
}

SwiftUI how to set image for NavigationBar titleView like in UIKit?

I want to set an image in the titleView of NavigationBar in SwiftUI, as we do in UIKit
navigationItem.titleView = UIImageView(image: UIImage(named: "logo"))
this is how we do it in UIKit.
anyone know how to do it?
Here's how to do it:
Add SwiftUIX to your project.
Set your custom title view via View.navigationBarTitleView(_:displayMode:)
Example code:
struct ContentView: View {
public var body: some View {
NavigationView {
Text("Hello World")
.navigationBarTitleView(MyView())
}
}
}
Simple, Just add your root view into ZStack with top alignment and add your custom center view after root view
struct CenterNavigattionBar: View {
var body: some View {
ZStack(alignment: .top){
//Root view with empty Title
NavigationView {
Text("Test Navigation")
.navigationBarTitle("",displayMode: .inline)
.navigationBarItems(leading: Text("Cancle"), trailing: Text("Done"))
}
//Your Custom Title
VStack{
Text("add title and")
.font(.headline)
Text("subtitle here")
.font(.subheadline)
}
}
}
}
Before Image
After Image
Just use a toolbar.
You can add any views
import SwiftUI
struct HomeView: View {
// MARK: - Initializer
init() {
let appearance = UINavigationBar.appearance()
appearance.isOpaque = true
appearance.isTranslucent = false
appearance.barTintColor = UIColor(named: "background")
appearance.shadowImage = UIImage()
}
// MARK: - View
// MARK: Public
var body: some View {
NavigationView {
VStack(spacing: 20) {
Text("Hello")
Text("Navigation Bar Test")
}
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(leading: leadingBarButtonItems, trailing: trailingBarButtonItems)
.toolbar {
ToolbarItem(placement: .principal) {
VStack {
Text("Title").font(.headline)
Text("Subtitle").font(.subheadline)
}
}
}
}
}
// MARK: Private
private var leadingBarButtonItems: some View {
Button(action: {
}) {
Text("Left Button")
.font(.system(size: 12, weight: .medium))
}
}
private var trailingBarButtonItems: some View {
HStack {
Button(action: {
}) {
Text("R1\nButton")
.font(.system(size: 12, weight: .medium))
.multilineTextAlignment(.center)
}
Button(action: {
}) {
Text("R2\nButton")
.font(.system(size: 12, weight: .medium))
.multilineTextAlignment(.center)
}
}
}
}
Currently, you can't.
There are two overloads for .navigationBarTitle(), taking either a Text view or a type conforming to StringProtocol. You can't even pass in a modified view like Text("Title").font(.body). This would be a great feature, I'd submit a feature request: http://feedbackassistant.apple.com
Maybe this works for you?
Basically:
Use GeometryReader to get the width of the screen
Have NavigationBarItems(leading: HStack {Spacer() Image("name").resizable().frame(width:..., height: ..., alignment: .center Spacer()}.frame(width:geometry.size.width)
Example code:
struct ContentView: View {
var body: some View {
NavigationView {
GeometryReader { geometry in
Text("Hello, world!")
.padding()
.navigationTitle("test")
.navigationBarItems(leading: HStack {
Spacer()
Image("money")
.resizable()
.frame(width: 50, height: 50, alignment: .center)
Spacer()
}
.frame(width: geometry.size.width)
)
}
}
}
}
Try this...
How to put a logo in NavigationView in swiftui?
This shows how to handle adding an Image to NavigationView in SwiftUI. Hope it helps.