feat: 弹窗阴影
This commit is contained in:
parent
bd603a7c19
commit
2d2a751a10
@ -903,7 +903,6 @@ struct BlindBoxView: View {
|
|||||||
memberDate: $memberDate
|
memberDate: $memberDate
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.shadow(color: .black.opacity(0.3), radius: 10, x: 5, y: 0)
|
|
||||||
.offset(x: showSettings ? UIScreen.main.bounds.width : 0)
|
.offset(x: showSettings ? UIScreen.main.bounds.width : 0)
|
||||||
.animation(.spring(response: 0.6, dampingFraction: 0.8), value: showSettings)
|
.animation(.spring(response: 0.6, dampingFraction: 0.8), value: showSettings)
|
||||||
|
|
||||||
|
|||||||
71
wake/Utils/SVGImageHtml.swift
Normal file
71
wake/Utils/SVGImageHtml.swift
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import WebKit
|
||||||
|
|
||||||
|
struct SVGImageHtml: UIViewRepresentable {
|
||||||
|
let svgName: String
|
||||||
|
|
||||||
|
func makeUIView(context: Context) -> WKWebView {
|
||||||
|
let webView = WKWebView()
|
||||||
|
webView.isOpaque = false
|
||||||
|
webView.backgroundColor = .clear
|
||||||
|
webView.scrollView.isScrollEnabled = false
|
||||||
|
webView.scrollView.contentInsetAdjustmentBehavior = .never
|
||||||
|
|
||||||
|
// 1. Get the URL for the SVG file
|
||||||
|
guard let path = Bundle.main.path(forResource: svgName, ofType: "svg") else {
|
||||||
|
print("❌ Cannot find SVG file: \(svgName).svg in bundle")
|
||||||
|
return webView
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileURL = URL(fileURLWithPath: path)
|
||||||
|
|
||||||
|
do {
|
||||||
|
// 2. Read the SVG content directly
|
||||||
|
let svgString = try String(contentsOf: fileURL, encoding: .utf8)
|
||||||
|
|
||||||
|
// 3. Create HTML with inline SVG for better reliability
|
||||||
|
let htmlString = """
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
|
<style>
|
||||||
|
body, html {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: transparent;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
svg {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
\(svgString)
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
// 4. Load the HTML with base URL as the main bundle's resource path
|
||||||
|
if let resourcePath = Bundle.main.resourceURL {
|
||||||
|
webView.loadHTMLString(htmlString, baseURL: resourcePath)
|
||||||
|
} else {
|
||||||
|
webView.loadHTMLString(htmlString, baseURL: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
print("❌ Error loading SVG file: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return webView
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIView(_ uiView: WKWebView, context: Context) {}
|
||||||
|
}
|
||||||
@ -8,7 +8,7 @@ struct SlideInModal<Content: View>: View {
|
|||||||
// 动画配置 - 更慢的动画
|
// 动画配置 - 更慢的动画
|
||||||
private let animation = Animation.spring(
|
private let animation = Animation.spring(
|
||||||
response: 0.8, // 增加响应时间使动画更慢
|
response: 0.8, // 增加响应时间使动画更慢
|
||||||
dampingFraction: 0.6, // 减少阻尼系数使弹跳更明显
|
dampingFraction: 1, // 减少阻尼系数使弹跳更明显
|
||||||
blendDuration: 0.8 // 增加混合时间使过渡更平滑
|
blendDuration: 0.8 // 增加混合时间使过渡更平滑
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,21 +28,35 @@ struct SlideInModal<Content: View>: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 弹窗内容
|
// 添加一个额外的容器来承载阴影
|
||||||
VStack(spacing: 0) {
|
ZStack(alignment: .leading) {
|
||||||
// 顶部安全区域占位
|
// 弹窗内容
|
||||||
Color.clear
|
VStack(spacing: 0) {
|
||||||
.frame(height: UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0)
|
// 顶部安全区域占位
|
||||||
|
Color.clear
|
||||||
|
.frame(height: UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0)
|
||||||
|
|
||||||
// 内容区域
|
// 内容区域
|
||||||
content()
|
content()
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
.padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0)
|
.padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0)
|
||||||
|
}
|
||||||
|
.frame(width: UIScreen.main.bounds.width * 0.8)
|
||||||
|
.frame(maxHeight: .infinity)
|
||||||
|
.background(Color(.systemBackground))
|
||||||
|
.edgesIgnoringSafeArea(.vertical)
|
||||||
}
|
}
|
||||||
.frame(width: UIScreen.main.bounds.width * 0.8)
|
// 在这里应用阴影
|
||||||
.frame(maxHeight: .infinity)
|
.background(
|
||||||
.background(Color(.systemBackground))
|
RoundedRectangle(cornerRadius: 0)
|
||||||
.edgesIgnoringSafeArea(.vertical)
|
.fill(Color(.systemBackground))
|
||||||
|
.shadow(
|
||||||
|
color: .black.opacity(0.3),
|
||||||
|
radius: 10,
|
||||||
|
x: 5,
|
||||||
|
y: 0
|
||||||
|
)
|
||||||
|
)
|
||||||
.offset(x: isPresented ? 0 : -UIScreen.main.bounds.width)
|
.offset(x: isPresented ? 0 : -UIScreen.main.bounds.width)
|
||||||
.zIndex(2)
|
.zIndex(2)
|
||||||
.transition(.move(edge: .leading))
|
.transition(.move(edge: .leading))
|
||||||
|
|||||||
@ -71,6 +71,9 @@ struct UserProfileModal: View {
|
|||||||
.foregroundColor(.blue)
|
.foregroundColor(.blue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onTapGesture {
|
||||||
|
Router.shared.navigate(to: .userInfo)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Image(systemName: "person.circle.fill")
|
Image(systemName: "person.circle.fill")
|
||||||
.resizable()
|
.resizable()
|
||||||
|
|||||||
@ -50,18 +50,17 @@ public struct AvatarPicker: View {
|
|||||||
if let selectedImage = selectedImage {
|
if let selectedImage = selectedImage {
|
||||||
Image(uiImage: selectedImage)
|
Image(uiImage: selectedImage)
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFill()
|
.aspectRatio(contentMode: .fill)
|
||||||
.frame(width: 225, height: 225)
|
.frame(width: 225, height: 225)
|
||||||
.scaleEffect(scaleFactor)
|
.clipShape(RoundedRectangle(cornerRadius: 20))
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 20 * scaleFactor))
|
|
||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: 20)
|
RoundedRectangle(cornerRadius: 20)
|
||||||
.stroke(Color.themePrimary, lineWidth: borderWidth)
|
.stroke(Color.themePrimary, lineWidth: borderWidth)
|
||||||
.scaleEffect(scaleFactor)
|
|
||||||
)
|
)
|
||||||
|
.scaleEffect(scaleFactor)
|
||||||
} else {
|
} else {
|
||||||
// Default SVG avatar with animated dashed border
|
// Default SVG avatar with animated dashed border
|
||||||
SVGImage(svgName: "IP")
|
SVGImageHtml(svgName: "IP")
|
||||||
.frame(width: 225, height: 225)
|
.frame(width: 225, height: 225)
|
||||||
.scaleEffect(scaleFactor)
|
.scaleEffect(scaleFactor)
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
|
|||||||
@ -599,7 +599,7 @@ struct UploadPromptView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
Button(action: { showMediaPicker = true }) {
|
Button(action: { showMediaPicker = true }) {
|
||||||
// 上传图标
|
// 上传图标
|
||||||
SVGImage(svgName: "IP")
|
SVGImageHtml(svgName: "IP")
|
||||||
.frame(width: 225, height: 225)
|
.frame(width: 225, height: 225)
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
.overlay(
|
.overlay(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user