我正在尝试重新创建此背景图像的图案,它看起来像云。有没有办法使用 SwiftUI 以编程方式完成此操作?
我知道如何添加渐变和颜色,图像文本和按钮也一样,但我不知道我是否可以通过编程获取图案,或者我是否必须获取此类图案的资产。我应该只获取图片吗?
我又犯了一个可能又愚蠢的错误。目前的问题是这样的。我正在使用 Apple 的 MKLocalSearchCompleter 获取餐厅的名称和地址。我还使用 SwiftData 存储所有内容。我在表单上显示了一个表单。在调用表单之前,varsity 显示正确的占位符文本。在这种情况下,“tacos”表示餐厅名称,“bell”表示地址。单击位置按钮后,您可以搜索并点击您想要的那个。点击按钮正在更新 review.resataurantName 和 review.location,因为我已将它们打印到控制台。但是它不会更新表单中的值。以下是我认为与该问题相关的代码。
以下是编辑餐食视图
import SwiftUI
import PhotosUI
import SwiftData
struct EditMealView: View {
@Binding var isPresentingNewMealView: Bool
@Environment(\.modelContext) private var modelContext
@State var mealImage: Image? = nil
@State private var selectedItem: PhotosPickerItem? = nil
@Bindable var review: Review
@State private var imageData: Data?
@State var selection: TextSelection? = nil
@State private var locationService = LocationService(completer: .init())
@State private var search: String = ""
@State private var isPresentingLocationView = false
// @State var chosenLocation: String? = AddressView($locationSubtitle)
let foodTypeList: [String] = ["Select One...", "Fast Food", "Vegetarian", "Central African", "East African", "North African", "Southern Africa", "West African", "Latin American", "Native American", "Canadian", "Carribean", "Mexican", "American", "Southern American", "Fusion", "Central American", "South American", "Chinese", "Japanese", "Korean", "Indian", "Middle Eastern", "Thai", "Turkish", "Vietnamese", "Italian", "French", "German", "Spanish", "Greek", "Romanian", "Russian", "Eastern European", "Scandinavian", "British", "Dutch", "Swedish", "Norwegian", "Icelandic", "Irish", "Polynesian", "Australian", "New Zealand", "Other"]
var body: some View {
NavigationStack {
ZStack {
LinearGradient(gradient: Gradient(colors: [Color(red: 0.9725490196078431, green: 0.9607843137254902, blue: 0.8627450980392157, opacity: 1), Color(red: 0.9137254901960784, green: 0.9254901960784314, blue: 0.8509803921568627, opacity: 1), Color(red: 0.9882352941176471, green: 0.7529411764705882, blue: 0.13333333333333333, opacity: 1), Color(red: 0.9450980392156862, green: 0.47058823529411764, blue: 0.08627450980392157, opacity: 1)]), startPoint: .top, endPoint: .bottom).ignoresSafeArea()
.navigationTitle("Add Meal")
VStack{
Form {
TextField("Menu Item", text: $review.foodName)
.listRowBackground(Color.clear)
.autocorrectionDisabled()
HStack {
Button(action: {isPresentingLocationView = true}
) {
Text("Location")
Image(systemName: "map")
}
.sheet(isPresented: $isPresentingLocationView) {
AddressView(isPresentingLocationView: $isPresentingLocationView, review: Review(foodName: "", restaurantName: "", location: "muncie", rating: 3, foodImage: nil, foodType: "", reviewNotes: "")//, //restaurantName: ""
)
}
VStack {
Text("\(review.restaurantName)")
.listRowBackground(Color.clear)
.autocorrectionDisabled()
Text("\(review.location)")
.listRowBackground(Color.clear)
.autocorrectionDisabled()
}
}
.listRowBackground(Color.clear)
Picker(selection: $review.foodType, label: Text("\(review.foodType)")) {
ForEach(0..<foodTypeList.count, id: \.self) {
Text(self.foodTypeList[$0])
.tag(self.foodTypeList[$0])
}
}
.listRowBackground(Color.clear)
HStack{
Spacer()
RatingView(rating: $review.rating)
.multilineTextAlignment(TextAlignment.center)
Spacer()
}
.buttonStyle(.plain)
.listRowBackground(Color.clear)
if let imageData = review.foodImage, let uiImage = UIImage(data: imageData) {
Image(uiImage: uiImage)
.resizable()
.scaledToFit()
}
PhotosPicker(selection: $selectedItem, matching: .images) {
Label("Select a photo", systemImage: "fork.knife")
}
.onChange(of: selectedItem, loadPhoto)
.listRowBackground(Color.clear)
.padding()
}
.listRowBackground(Color.clear)
.padding()
Spacer()
HStack {
Text("Notes")
.padding()
.font(.headline)
Spacer()
}
TextEditor(text: $review.reviewNotes, selection: $selection)
.background(Color.white.opacity(0.40))
}
.scrollDismissesKeyboard(.immediately)
}
.scrollContentBackground(.hidden)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Dismiss") {
isPresentingNewMealView = false
}
}
ToolbarItem(placement: .confirmationAction) {
Button("Add") {
let newReview = review
modelContext.insert(newReview)
isPresentingNewMealView = false
}
}
}
}
}
func loadPhoto() {
Task { @MainActor in
review.foodImage = try await selectedItem?.loadTransferable(type: Data.self)
}
}
}
地址视图
import SwiftUI
import MapKit
import SwiftData
struct AddressView: View {
@State private var locationService = LocationService(completer: .init())
@State private var search: String = ""
@Binding var isPresentingLocationView: Bool
@Bindable var review: Review
var body: some View {
NavigationStack {
ZStack {
LinearGradient(gradient: Gradient(colors: [Color(red: 0.9725490196078431, green: 0.9607843137254902, blue: 0.8627450980392157, opacity: 1), Color(red: 0.9137254901960784, green: 0.9254901960784314, blue: 0.8509803921568627, opacity: 1), Color(red: 0.9882352941176471, green: 0.7529411764705882, blue: 0.13333333333333333, opacity: 1), Color(red: 0.9450980392156862, green: 0.47058823529411764, blue: 0.08627450980392157, opacity: 1)]), startPoint: .top, endPoint: .bottom).ignoresSafeArea()
.navigationTitle("Add Location")
VStack {
HStack {
Image(systemName: "magnifyingglass")
TextField("Search for a restaurant", text: $search)
.autocorrectionDisabled()
}
Spacer()
List {
ForEach(locationService.completions) { completion in
Button(action: {didTapOnCompletion(completion)}) {
VStack(alignment: .leading, spacing: 4) {
Text(completion.title)
.font(.headline)
.fontDesign(.rounded)
Text(completion.subTitle)
}
}
.listRowBackground(Color.clear)
}
}
.listStyle(.plain)
.scrollContentBackground(.hidden)
}
.onChange(of: search) {
locationService.update(queryFragment: search)
}
.padding()
.presentationBackground(.regularMaterial)
.presentationBackgroundInteraction(.enabled(upThrough: .large))
}
}
}
private func didTapOnCompletion(_ completion: SearchCompletions) {
isPresentingLocationView = false
review.restaurantName = completion.title
review.location = completion.subTitle
print("this is the name \(review.restaurantName) and this is the location \(review.location)")
}
}
在 XCTest 中我可以用来expectation(forNotification:)
测试 NSNotificationCenter 发布通知如下:
func testLoginNotification() {
let notificationExpectation = expectation(forNotification: .login, object: nil, handler: nil)
methodPostNotificationLogin()
wait(for: [notificationExpectation], timeout: 2)
}
如何在快速测试中做到这一点?
@Test fun loginNotification() {
#expect()
}
我以为Combine
应该是线程安全的,尽管文档很少。我注意到,当在CurrentValueSubject
初始化线程以外的线程上发布时,某些内部会发生持续崩溃dispatch_assert_queue_fail
。
这发生在启用 的包中StrictConcurrency
。如果我禁用它,崩溃就会消失。有趣的是,当我把它放回去时,一致的崩溃不会再出现,直到我清除我的DerivedData
。
Publisher
这是预料之中的吗?如果我想要一个线程安全的解决方案来将值发布到同时保持严格的并发性,建议怎么做?
完整代码:
@preconcurrency import Combine
final class CombineTests: XCTestCase {
@MainActor
func testExample() throws {
let exp = expectation(description: "received 1")
let currentValue = CurrentValueSubject<Int, Never>(0)
let cancellable = currentValue.eraseToAnyPublisher().sink { value in
if value == 1 {
exp.fulfill()
}
}
DispatchQueue.global(qos: .background).async {
currentValue.send(1)
}
wait(for: [exp], timeout: 1)
_ = cancellable
}
}
MyUIClass
我有一个孤立的班级@MainActor
:
@MainActor
class MyUIClass {
func show(object: Any?) {}
}
要使用该show
函数,需要在MainActor
上下文中访问它:
class Santa {
@MainActor
func present1(gift: Any?) {
MyUIClass().show(object: gift) // ✅
}
func present2(gift: Any?) {
Task { @MainActor in
MyUIClass().show(object: gift) // 💥 Task or actor isolated value cannot be sent; this is an error in the Swift 6 language mode."
}
}
}
我不明白的是,当我尝试在块gift
内使用时,我收到警告Task
present2
无法发送任务或参与者隔离值;这是 Swift 6 语言模式下的错误。
在第一个函数中present1
,参数gift: Any?
没有被隔离到MainActor
(对吗?)并且不是Sendable
,但没有错误。然而,错误只发生在第二种情况下,在Task
块内。
我知道标题有点奇怪。我理解@MainActor
函数只能在MainActor
上下文中执行,并且Task { @MainActor in }
可以在任何上下文中执行。
尽管如此,在这两种情况下(present1 / present2
),似乎在上下文MyUIClass().show(object: gift)
中执行了MainActor
,那么为什么错误行为会有差异呢?
我使用 FirebaseAnalytics 来View.analyticsScreen(type(of: self))
记录我的视图。这很好用:当我有一个名为的视图时MyBeautifulView
,就会记录该视图。但是,我不必import FirebaseAnalytics
每次都这样做,而是继续在 View 上创建扩展
func analyticsScreen() -> some View {
analyticsScreen(name: "\(type(of: self))")
}
我注意到,使用上述方法时,输出type(of: self)
类似于
_ConditionalContent<_ConditionalContent<ModifiedContent<ModifiedContent<ModifiedContent<ModifiedContent<ModifiedContent<ModifiedContent<List<Never,TupleView<(Optional<Section<Text,ForEach<Array,Optional,ModifiedContent<ModifiedContent...
我原本希望它是视图的名称。为什么它不显示名称?我怎样才能获得我想要的行为?这是个功能吗?(当然我可以通过参数传递结构名称,但我想知道是否有更好的方法)。
另外,这不是 Firebase 问题,这就是为什么我没有将其标记为 Firebase 问题。如果我执行 ,也会发生这种情况print(type(of: self))
。
如何使某些点能够像视频中那样通过一组视图边缘进行拖动:
而不是自由拖拽:
我甚至不知道从哪里开始,因为没有 GeometryReader 我就无法测量视图的边界。
然而,GeometryReader 在这种情况下并不合适,因为这些是位于不同图层上的不同视图。
示例视图:
struct ContentView: View {
@State var point: CGPoint = .zero
var body: some View {
ZStack {
//foreach Nodes
NodeView()
NodeView()
.offset(x:0, y:100)
//foreach Points
BezierPoint(p1: $point)
}
}
}
struct NodeView : View {
// var nodeViewModel: NodeViewModel
// with exact location in space
var body: some View {
Text("Business")
.multilineTextAlignment(.center)
.foregroundStyle(.red)
.shadow(color: .black, radius: 2 )
.frame(minHeight: 40)
.padding( EdgeInsets(horizontal: 20, vertical: 14) )
.background {
// ANY Shape can be here
RoundedRectangle(cornerRadius: 10)
}
}
}
struct BezierPoint: View {
@Binding var p1: CGPoint
let pointsSize: CGFloat = 15
var body: some View {
GeometryReader { reader in
ControlPointHandle(size: pointsSize)
.offset( CGSize(width: p1.x + reader.size.width/2, height: p1.y + reader.size.height/2) )
.gesture(
DragGesture()
.onChanged { value in
self.p1 = value.location.relativeToCenter(of: reader.size, minus: true)
}
)
}
}
}
private struct ControlPointHandle: View {
let size: CGFloat
var body: some View {
Circle()
.frame(width: size, height: size)
.overlay(
Circle()
.stroke(Color.blue, lineWidth: 2)
)
.offset(x: -size/2, y: -size/2)
}
}
fileprivate extension CGPoint {
func relativeToCenter(of size: CGSize, minus: Bool = false) -> CGPoint {
let a: CGFloat = minus ? -1 : 1
return CGPoint(x: x + a * size.width/2, y: y + a * size.height/2)
}
}
我有一个 SwiftUI 视图,我希望能够显示许多其他不同视图之一:
struct View1: View {
var body: some View {
Text("View1")
}
}
struct View2: View {
var body: some View {
Text("View2")
}
}
struct View3: View {
var body: some View {
Text("View3")
}
}
struct View4: View {
var body: some View {
Text("View4")
}
}
struct ContentView: View {
@State var showView1 = false
@State var showView2 = false
@State var showView3 = false
@State var showView4 = false
@ViewBuilder var body: some View {
if (showView1) {
View1()
} else if (showView2) {
View2()
} else if (showView3) {
View3()
} else if (showView4) {
View4()
} else {
VStack {
Button ("Show View1") {
showView1 = true
}
Button ("Show View2") {
showView2 = true
}
Button ("Show View3") {
showView3 = true
}
Button ("Show View4") {
showView4 = true
}
}
}
}
}
但必须有某种方法来使用元类来避免长if else
链。
所以我尝试了类似的事情:
struct ContentView: View {
@State var showType: View.Type? = nil
@ViewBuilder var body: some View {
if let showType = showType {
showType.init()
} else {
VStack {
Button ("Show View1") {
showType = View1.type
}
Button ("Show View2") {
showType = View2.type
}
Button ("Show View3") {
showType = View3.type
}
Button ("Show View4") {
showType = View4.type
}
}
}
}
}
然而,这似乎不太正确,因为我收到此showType
声明错误:
协议“View”只能用作通用约束,因为它具有自身或关联类型要求
我相信这是因为View
它是一种协议而不是具体类型,但我不确定如何解决这个问题。在 Swift(UI) 中执行此操作的正确语法是什么?
我处理的数字很少,但我很挣扎,因为我无法在屏幕上显示它们。
我所做的基本上是创建一个浮点数潜水整数
let minute = Calendar.current.component(.minute, from: Date())
let JDminute = Float(minute/1440)
而不是使用在屏幕上显示它
Text(JDminute, format: .number.precision(.fractionLength(5)))
或常绿
Text(String(format: "Julian date: %.5f", turnintoJulian(date: outputDate)))
但我得到的只是零。
我想问题在于我如何创建浮点数,但我尝试了不同的方法,但总是得到相同的结果:0
正确的做法是怎样的?