Interact with item inside widgets iOS14 - swiftui

I am trying to create a medium Widget like in YouTube Music, but I am don't understand how to create an interaction with the particular item in a Widget. How my app should understand when user press on first or second item and then how I am must handle this action inside app. My app use Swift not SwiftUI, only for a Widget I use SwiftUI. In past I didn't have experience with a SwityUI.
My code for Widget:
struct WidgetTestEntryView : View {
var entry: Provider.Entry
var body: some View {
VStack {
HStack(spacing: 100){
Text("Favourite").foregroundColor(.white).font(.system(size: 16, weight: .bold, design: .default))
Image("Label").resizable().frame(width: 80, height: 15, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
}.frame(maxWidth: .infinity, maxHeight: 50, alignment: .center).background(Color.black).offset(y: -9)
HStack {
Spacer()
Button(action: {}) {
Image("").resizable().frame(width: 70, height: 70)
.cornerRadius(10)
.background(Color(red: 0.218, green: 0.215, blue: 0.25))
}.cornerRadius(10).onTapGesture {
let a = ViewController()
a.data.text = "Tap"
}
Button(action: {}) {
Image("").resizable().frame(width: 70, height: 70)
.cornerRadius(10)
.background(Color(red: 0.218, green: 0.215, blue: 0.25))
}.cornerRadius(10).onTapGesture {
let a = ViewController()
a.data.text = "Tap"
}
Button(action: {}) {
Image("").resizable().frame(width: 70, height: 70)
.cornerRadius(10)
.background(Color(red: 0.218, green: 0.215, blue: 0.25))
}.cornerRadius(10).onTapGesture {
let a = ViewController()
a.data.text = "Tap"
}
Button(action: {}) {
Image("").resizable().frame(width: 70, height: 70)
.cornerRadius(10)
.background(Color(red: 0.218, green: 0.215, blue: 0.25))
}.cornerRadius(10).onTapGesture {
let a = ViewController()
a.data.text = "Tap"
}
Spacer().frame(width: 10, height: 10, alignment: .center)
}.background(Color(red: 0.118, green: 0.118, blue: 0.15)).offset(y: -9)
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center).background(Color(red: 0.118, green: 0.118, blue: 0.15))
}
}

You can do this using Link in SwiftUI.
Link(destination: url, label: {
// add your UI components in this block on which you want to perform click action.
}
url: Provide a unique string to identify widget action in the main app.
Now In the main app, use below method in AppDelegate.swift file:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
}
In this method, you can identify the URL that comes from the WidgetKit action.

Related

Why are my SwiftUI Links not respecting frame maxWidth?

I am trying to make a list of buttons/links that all have the same width, regardless of text content. Unfortunately, it seems like the Link() items do not respect the .frame(maxWidth) settings and just do whatever they want. I cannot find any resources online that describe how to do this, so I am turning here to see if anyone can help me determine the best way to line these up to be the same width.
My code below:
VStack(alignment: .center) {
Button {
bypass.toggle()
} label: {
Text("Push to Bypass")
.frame(maxWidth: .infinity)
}
.font(.title2)
.fontWeight(.bold)
.buttonStyle(.borderedProminent)
.foregroundColor(Color(hue: 1.0, saturation: 1.0, brightness: 1.0, opacity: 0.825))
.tint(Color(red: 0.005, green: 0.669, blue: 0.791))
Link("Settings", destination: URL(string: "http://192.168.10.1/TSettings")!)
.frame(maxWidth: .infinity)
.font(.title2)
.fontWeight(.bold)
.buttonStyle(.borderedProminent)
.foregroundColor(.white)
.tint(Color(red: 0.005, green: 0.669, blue: 0.791))
Link("Recorder Settings", destination: URL(string: "http://192.168.10.1/VB")!)
.frame(maxWidth: .infinity)
.font(.title2)
.fontWeight(.bold)
.buttonStyle(.borderedProminent)
.foregroundColor(.white)
.tint(Color(red: 0.005, green: 0.669, blue: 0.791))
Link("Download Data", destination: URL(string: "http://192.168.10.1/ddownload")!)
.frame(maxWidth: .infinity)
.font(.title2)
.fontWeight(.bold)
.buttonStyle(.borderedProminent)
.foregroundColor(.white)
.tint(Color(red: 0.005, green: 0.669, blue: 0.791))
Link("Download Audio", destination: URL(string: "http://192.168.10.1/generate")!)
.frame(maxWidth: .infinity)
.font(.title2)
.fontWeight(.bold)
.buttonStyle(.borderedProminent)
.foregroundColor(.white)
.tint(Color(red: 0.005, green: 0.669, blue: 0.791))
}.fixedSize(horizontal: true, vertical: false)
What it currently looks like:
Add this to your struct:
#Environment(\.openURL) var openURL
Use a regular button and apply the desired modifiers:
Button {
openURL(URL(string: "http://192.168.10.1/TSettings")!)
} label: {
Text("Settings")
.frame(maxWidth: .infinity)
}
Get rid of .fixedSize(horizontal: true, vertical: false) at the end and use Link with label: init:
Link(destination: URL(string: "http://192.168.10.1/TSettings")!) {
Text("Settings")
.frame(maxWidth: .infinity)
}
.font(.title2)
.fontWeight(.bold)
.buttonStyle(.borderedProminent)
.foregroundColor(.white)
.tint(Color(red: 0.005, green: 0.669, blue: 0.791))

SwiftUI define NavigationLink destination conditionally

I’m new at SwiftUI and I’ve a doubt. I wonder if anyone can help me.
I’ve a screen with two exact buttons (except for the text that they display and the view which they lead to). When they are tapped, each button leads the user to a new given view.
The code is this:
HStack {
NavigationLink(destination: PlayView()) {
ButtonTextView(buttonText: "Play")
}
.frame(width: 120, height: 40, alignment: .center)
.background(Color(red: 242/255, green: 242/255, blue: 242/255, opacity: 1))
.cornerRadius(10.0)
Spacer()
NavigationLink(destination: RankingView()) {
ButtonTextView(buttonText: "Ranking")
}
.frame(width: 120, height: 40, alignment: .center)
.background(Color(red: 242/255, green: 242/255, blue: 242/255, opacity: 1))
.cornerRadius(10.0)
}
And I want to be able to do something like this:
HStack {
ButtonView(buttonIdentifier: "Play")
Spacer()
ButtonView(buttonIdentifier: "Ranking")
}
Where ButtonView is defined like so:
NavigationLink(destination: PlayView()) {
ButtonTextView(buttonText: "Play")
}
.frame(width: 120, height: 40, alignment: .center)
.background(Color(red: 242/255, green: 242/255, blue: 242/255, opacity: 1))
.cornerRadius(10.0)
So, basically, I don’t repeat code and have a much more readable file.
The problem comes when I’ve to define the destination of the NavigationLink. There is some way I can set the destination to be a variable and pass it when I call ButtonView()? So that if the buttonIdentifier is "Play", destination is PlayView() (like above) and if is "Ranking", then is RankingView().
I hope I’ve been able to explain my problem clearly (i’m not a native speaker…)
Any help is appreciated. Thanks a lot :)!
struct ButtonView<Destination>: View where Destination : View {
let buttonText: String
let destination: Destination
init(buttonText: String, #ViewBuilder destination: #escaping () -> Destination) {
self.buttonText = buttonText
self.destination = destination()
}
var body: some View {
NavigationLink(destination: destination) {
Text(buttonText)
}
.frame(width: 120, height: 40, alignment: .center)
.background(Color(red: 242/255, green: 242/255, blue: 242/255, opacity: 1))
.cornerRadius(10.0)
}
}
Use like:
ButtonView(buttonText: "Play") {
PlayView()
}

iOS 15.3 has broken SwiftUI layout

I just upgraded my phone from 15.x (I think 15.2) to 15.3, and my SwiftUI layout is broken, seems to be a List border issue.
Here is the code, followed by a screen shot of normal behavior (13.x to 15.x) and then what I see in 15.3
struct SessionView: View {
var title: String
var panel: Int
var index: Int
var range: [Int] = [0,2,3,4]
#EnvironmentObject var state: MainViewModel
#EnvironmentObject var content: ContentViewModel
#ViewBuilder
var body: some View {
VStack() {
// -- Header
HStack() {
Text(" ")
Image(self.state.panelIcon(panel: panel)).resizable().frame(width: 12.0, height: 12.0)
Text(title)
Spacer()
}.padding(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0))
.background(Color(red: 0.9, green: 0.9, blue: 0.9))
.onTapGesture {
showDetailDialog()
}
// -- Rows
List {
ForEach(0..<5) { i in
if i != 1 { // Skip IP address
HStack(alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/, spacing: -4, content: {
Text("\(state.keyValues[i+index]!.key)").frame(minWidth: 120, maxHeight: 20, alignment: .leading)
.font(Font.system(size: 15, design: .default)).padding(0)
Text("\(state.keyValues[i+index]!.value)").frame(maxHeight: 20, alignment: .leading)
.font(Font.system(size: 15, design: .default)).padding(0)
}).frame(height: 10)
}
}
}.environment(\.defaultMinListRowHeight, 10)
.frame(height: 4*20+20)
.listStyle(DefaultListStyle()).environment(\.defaultMinListRowHeight, 8).onAppear {
UITableView.appearance().isScrollEnabled = false
}.layoutPriority(1)
}.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color(red: 0.8, green: 0.8, blue: 0.8), lineWidth: 1.25)
).background(Color.white)
}
.
.
.
}
I had this problem too. However, it is not a border issue. When you updated your iPhone, the default list style for SwiftUI changed. The old default style is now called PlainListStyle(). Use that instead to get the old look back.
List {
}.listStyle(PlainListStyle())

New to SwiftUI trying to understand layout changes without (I guess it doesn't have ) Layout constraints

The problem is I don't understand how to fix my Content View to the screen and handle things like rotation or even how to make other screen sizes dynamic. Below is my entire View code. When I create a new app out of the box it looks fine in preview, the way I like it. However, screen sizes change and all I know to do is put magic numbers, because there is no superview or frame to size from. Also I can't seem to clip at screen edges. So my background view is way to big. How is this handled in SwiftUI?
import SwiftUI
import AuthenticationServices
struct LoginScreen: View {
var body: some View {
ZStack{
backgroundLayout
loginLayout
}
}
var loginLayout: some View {
VStack {
welcomeText
signInWithAppleButton
}
}
var backgroundLayout: some View {
ZStack{
Rectangle()
.foregroundColor(.init(.sRGB, red: 0, green: 0.750, blue: 0.750, opacity: 0.25))
Circle()
.foregroundColor(.blue)
.offset(x: 200, y: -200)
.aspectRatio(1/3, contentMode: .fill)
.clipped()
Circle()
.foregroundColor(.green)
.offset(x: -200, y: 200)
.aspectRatio(1/3, contentMode: .fill)
.clipped()
}
}
var welcomeText: some View {
HStack {
VStack {
Text("Sign Up")
.font(.largeTitle)
.bold()
Text("Sign In")
.font(.title)
.bold()
Text("Get Started!")
.font(.title2)
.bold()
}
.offset(x: -100, y: -150)
}
}
var signInWithAppleButton: some View {
SignInWithAppleButton(
.continue,
onRequest: { request in
request.requestedScopes = [.fullName, .email]
},
onCompletion: { result in
switch result {
case .success (let authenticationResults):
print("Authorization successful! :\(authenticationResults)")
case .failure(let error):
print("Authorization failed: " + error.localizedDescription)
}
}
)
.offset(x: 0, y: 150)
.frame(width: 200, height: 50, alignment: .center)
}
}
struct Login_Preview: PreviewProvider {
static var previews: some View {
LoginScreen()
}
}
After trying several things this is all I could come up with. It's not perfect and certainly not ideal, but at least it work somewhat as expected.
import SwiftUI
import AuthenticationServices
struct LoginScreen: View {
var body: some View {
loginLayout.background(backgroundLayout)
.statusBar(hidden: true)
}
var loginLayout: some View {
GeometryReader {geo in
VStack {
welcomeText.padding(.top, geo.size.height / 10)
Spacer()
signInWithAppleButton
.padding(.bottom, geo.size.height / 10)
}
}
}
var backgroundLayout: some View {
GeometryReader { geo in
let circleSize = min(geo.size.width, geo.size.height)
ZStack {
Rectangle()
.foregroundColor(.init(.sRGB, red: 0, green: 0.750, blue: 0.750, opacity: 0.25))
Circle()
.foregroundColor(.blue)
.offset(x: circleSize / 1.75,
y: -geo.size.height / 2.5)
.frame(width: circleSize, height: circleSize, alignment: .center)
Circle()
.foregroundColor(.green)
.offset(x: -circleSize / 1.75, y: geo.size.height / 2.5)
.frame(width: circleSize, height: circleSize, alignment: .center)
}
.frame(width: geo.size.width, height: geo.size.height, alignment: .top)
}
}
var welcomeText: some View {
GeometryReader { geo in
HStack {
VStack {
Text("Sign Up")
.font(.largeTitle)
.bold()
Text("Sign In")
.font(.title)
.bold()
Text("Get Started!")
.font(.title2)
.bold()
}
.padding(.leading, geo.size.width / 8)
Spacer()
}
}
}
var signInWithAppleButton: some View {
SignInWithAppleButton(
// Add this below for Continue with Apple button
.continue,
onRequest: { request in
request.requestedScopes = [.fullName, .email]
},
onCompletion: { result in
switch result {
case .success (let authenticationResults):
print("Authorization successful! :\(authenticationResults)")
case .failure(let error):
print("Authorization failed: " + error.localizedDescription)
}
}
)
.frame(width: 200, height: 50, alignment: .center)
}
}
struct Login_Preview: PreviewProvider {
static var previews: some View {
LoginScreen()
}
}

why is Color view transparent in swiftUI

i have this card thing in the picture to build, here're the code:
struct ArticleCard: View {
var article: Article
var body: some View {
ZStack {
Color.white
.border(Color(red: 0, green: 0, blue: 0, opacity: 0.2))
.shadow(color: Color(red: 0, green: 0, blue: 0, opacity: 0.2), radius: 0.0, x: 5, y: 5)
VStack{
Text(article.title)
.padding(.top, 5)
.padding(.leading)
.padding(.trailing)
.font(.title2)
Image(article.coverUrl).resizable()
.frame(height: 200)
.padding(.leading)
.padding(.trailing)
Text(article.title)
.padding(.leading)
.padding(.trailing)
.padding(.bottom)
}
}
}
}
in the ZStack i put a Color.white there as a background of the card, and give this color view a shadow, but the color seems to be transparent, therefor i got unwanted lines on the top and the left inside the borders, how do i get rid of them?
You are using border and shadow in wrong place, try this:
PS: there is no reason of using Color(red: 0, green: 0, blue: 0, opacity: 0.2) you can use this: Color.black.opacity(0.2)
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
Color.white
VStack {
Text("article.title")
.padding()
Image(systemName: "star")
.resizable()
.scaledToFit()
Text("article.title")
.padding()
}
}
.frame(width: 300, height: 300, alignment: .center)
.border(Color(red: 0, green: 0, blue: 0, opacity: 0.2))
.compositingGroup()
.shadow(color: Color(red: 0, green: 0, blue: 0, opacity: 0.2), radius: 0.0, x: 5, y: 5)
}
}
I refactored your code for better and less code as possible you can use Image instead of Text in your App.
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Text("your custom Text")
.padding()
Text("🥩")
.font(Font.system(size: 150))
Text("your custom Text")
.padding()
}
.frame(width: 300, height: 300, alignment: .center)
.background(Color.white)
.border(Color.black.opacity(0.2))
.compositingGroup()
.shadow(color: Color.black.opacity(0.2), radius: 0.0, x: 5, y: 5)
}
}