Open fatbobman opened 2 years ago
如果要让底部的内容做成圆角呢?我尝试了以下代码是不行的
.safeAreaInset(edge: .bottom, spacing: 0) {
Text("底部状态条")
.font(.title3)
.foregroundColor(.indigo)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 40)
.padding()
.background(.gray)
.cornerRadius(30)
圆角应该是对 background 进行了clip, 导致不会再自动向非safeArea区域自动扩展了。
没有太看懂你的意思,我用了你提供的代码
safeArea 区域已经被设置为圆角
@fatbobman 没有太看懂你的意思,我用了你提供的代码
safeArea 区域已经被设置为圆角
是的,我的意思是使用圆角修饰后,下面的非安全区域没有被自动填充。如果去掉圆角,则会自动填充。是不是因为圆角对背景进等了clip的缘故?那怎样实现这个圆角(只需要看到上面的圆角, 下面非安全区背景自动扩展)呢?
换成 .containerShape(RoundedRectangle(cornerRadius: 30))
就可以了
明白了。非常感谢。看了你写的文章,受益非浅。
不客气
@fatbobman 发现一个奇怪的问题. 我试图在你最后的 demo 中添加一个 Menu, 当键盘弹出时, 出现了奇怪的layout问题. 代码如下:
HStack(alignment: .firstTextBaseline) { // 即便修改alignment也无用
// 输入框
TextField("输入", text: $text)
.focused($focused)
.textFieldStyle(.roundedBorder)
.padding(.horizontal, 10)
.padding(.top, 10)
.onSubmit {
addMessage()
scrollToBottom()
}
.onChange(of: focused) { value in
if value {
scrollToBottom()
}
}
// 添加 Menu 后, 键盘弹出后, Menu 出现 layout 问题
Menu {
Text("A")
Text("B")
Text("C")
} label: {
Image(systemName: "lock")
.frame(width: 60, height: 60) // 似乎和设置 frame 有关系?
}
// 回复按钮
Button("回复") {
addMessage()
scrollToBottom()
focused = false
}
.buttonStyle(.bordered)
.controlSize(.small)
.tint(.green)
}
.padding(.horizontal, 30)
}
换成
.containerShape(RoundedRectangle(cornerRadius: 30))
就可以了
这里修改cornerRadius为更小的值(比如10), 但为什么并不起作用呢? 圆角依旧很大.
HStack(alignment: .firstTextBaseline) {
目前的解决方法是,取消对齐设定
HStack(alignment: .firstTextBaseline) {
// 改成
HStack {
换成
.containerShape(RoundedRectangle(cornerRadius: 30))
就可以了这里修改cornerRadius为更小的值(比如10), 但为什么并不起作用呢? 圆角依旧很大.
我这里正常
HStack(alignment: .firstTextBaseline) {
目前的解决方法是,取消对齐设定
HStack(alignment: .firstTextBaseline) { // 改成 HStack {
奇怪, 我在模拟器和真机上试了好像都不行, 当键盘弹出时, menu跑飞了.
整个测试代码如下:
struct ChatBarDemo: View {
@State var messages: [Message] = (0...60).map { Message(text: "message:\($0)") }
@State var text = ""
@FocusState var focused: Bool
@State var bottomTrigger = false
var body: some View {
NavigationView {
ScrollViewReader { proxy in
List {
ForEach(messages) { message in
Text(message.text)
.id(message.id)
}
}
.listStyle(.inset)
.safeAreaInset(edge: .bottom) {
ZStack(alignment: .top) {
Color.clear
Rectangle().fill(.secondary).opacity(0.3).frame(height: 0.6) // 上部线条
HStack {
// 输入框
TextField("输入", text: $text)
.focused($focused)
.textFieldStyle(.roundedBorder)
.padding(.horizontal, 10)
.padding(.top, 10)
.onSubmit {
addMessage()
scrollToBottom()
}
.onChange(of: focused) { value in
if value {
scrollToBottom()
}
}
// 添加 Menu 后, 键盘弹出后, Menu 出现 layout 问题
Menu {
Text("A")
Text("B")
Text("C")
} label: {
Image(systemName: "lock")
.frame(width: 60, height: 60) // 似乎和设置 frame 有关系?
}
// 回复按钮
Button("回复") {
addMessage()
scrollToBottom()
focused = false
}
.buttonStyle(.bordered)
.controlSize(.small)
.tint(.green)
}
.padding(.horizontal, 30)
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 53)
.background(.regularMaterial)
.containerShape(RoundedRectangle(cornerRadius: 2, style: .continuous))
.shadow(color: .yellow, radius: 10, x: 0, y: 5)
}
.onChange(of: bottomTrigger) { _ in
withAnimation(.spring()) {
if let last = messages.last {
proxy.scrollTo(last.id, anchor: .bottom)
}
}
}
.onAppear {
if let last = messages.last {
proxy.scrollTo(last.id, anchor: .bottom)
}
}
}
.navigationBarTitle("SafeArea Chat Demo")
}
}
func scrollToBottom() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
bottomTrigger.toggle()
}
}
func addMessage() {
if !text.isEmpty {
withAnimation {
messages.append(Message(text: text))
}
text = ""
}
}
}
struct Message: Identifiable, Hashable {
let id = UUID()
let text: String
}
圆角我设置为2, 但结果如下:
非常感谢,解决了我的大问题。不过现在用 keyWindow
会导致 preview 报错,因为 UIApplication
已经有 keyWindow
这个属性在了。换成 currentWindow
可以解决:
extension UIApplication {
- var keyWindow: UIWindow? {
+ var currentWindow: UIWindow? {
connectedScenes
.compactMap {
$0 as? UIWindowScene
}
.flatMap {
$0.windows
}
.first {
$0.isKeyWindow
}
}
}
private struct SafeAreaInsetsKey: EnvironmentKey {
static var defaultValue: EdgeInsets {
- UIApplication.shared.keyWindow?.safeAreaInsets.swiftUiInsets ?? EdgeInsets()
+ UIApplication.shared.currentWindow?.safeAreaInsets.swiftUiInsets ?? EdgeInsets()
}
}
非常感谢,解决了我的大问题。不过现在用
keyWindow
会导致 preview 报错,因为UIApplication
已经有keyWindow
这个属性在了。换成currentWindow
可以解决:extension UIApplication { - var keyWindow: UIWindow? { + var currentWindow: UIWindow? { connectedScenes .compactMap { $0 as? UIWindowScene } .flatMap { $0.windows } .first { $0.isKeyWindow } } } private struct SafeAreaInsetsKey: EnvironmentKey { static var defaultValue: EdgeInsets { - UIApplication.shared.keyWindow?.safeAreaInsets.swiftUiInsets ?? EdgeInsets() + UIApplication.shared.currentWindow?.safeAreaInsets.swiftUiInsets ?? EdgeInsets() } }
谢谢你的反馈。 UIApplication 的 keyWindow 应该在 iOS 13 中软废弃了。不过确实可能导致其他的问题。我调整一下代码。
我观察滚动到底部这块的代码是通过延迟才做到的,有没有什么办法可以让滚动区域是和键盘的弹出是同步的?
func scrollToBottom() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
bottomTrigger.toggle()
}
}
我今天研究了一下各种软件的聊天界面,都是可以做到键盘拉起的时候消息始终和底部对齐的,但是相关的资料确实太少了,望大佬赐教,🙏
而且 scrollDismissesKeyboard 设为 .interactively 的时候,safeAreaInset 是有 bug 的: https://stackoverflow.com/questions/73770425/scrollview-scrolldismisseskeyboard-interactively-feels-weird
我看到有一个解决方式,似乎非常的复杂,所以还没有尝试 https://github.com/frogcjn/BottomInputBarSwiftUI/blob/main/BottomInputBarSwiftUI/BottomBar/UIBottomBar.Constraints.swift
还有一个类似的 stackoverflow 问题: https://stackoverflow.com/questions/45708312/detect-keyboard-height-while-uiscrollview-is-scrolled-down-and-the-keypad-is-bei
时间有点久了,一些细节记不清了。 这篇文章是介绍 safearea 的,文章中的例子主要是演示 safearea 的用法。因此在获取焦点后会修改两次状态。在 SwiftUI 中,在一个 loop 中如果修改多个状态有时会出现问题。因此在两种状态之间添加了延迟。 如果想尝试同步弹出,驱动的方式肯定要调整。 另外,目前例子里通过给所有 row 添加 id 的方式也不是太好的方式,只滚动到底部的话,可以尝试只在最下面添加一个包含 id 的底部 row,另一篇介绍 List 数据集优化的文章中有提及。 总之,想实现同时弹出的效果肯定是可以的,但用纯 SwiftUI 的方式会有些问题,甚至在键盘弹出时,safearea 的区域也会有一点延迟。这是当前响应式框架的问题。
2024年2月27日 19:45,07akioni @.***> 写道:
我观察滚动到底部这块的代码是通过延迟才做到的,有没有什么办法可以让滚动区域是和键盘的弹出是同步的?
func scrollToBottom() { DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { bottomTrigger.toggle() } } — Reply to this email directly, view it on GitHub https://github.com/fatbobman/blogComments/issues/128#issuecomment-1966368816, or unsubscribe https://github.com/notifications/unsubscribe-auth/ANIYIGLV2BXZX36EBKNFPCDYVXBMVAVCNFSM5IPWITB2U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOJWGYZTMOBYGE3A. You are receiving this because you were mentioned.
给所有 row 添加 id 的方式也不是太好的方式,只滚动到底部的话,可以尝试只在最下面添加一个包含 id 的底部 row,另一篇介绍 List 数据集优化的文章中有提
感觉 SwiftUI 还是任重道远。主要是我自己观察大厂的 app 都能做到同步的表现,但是搜索却很难找到答案,这个交互对于一个初学者来说感觉确实有点核心机密的程度了。
https://www.fatbobman.com/posts/safeArea/
Safe Area(安全区域)是指不与导航栏、标签栏、工具栏或其他视图控制器提供的视图重叠的内容空间。本文将探讨如何在 SwiftUI 中获取 SafeAreaInsets、将视图绘制到安全区域之外、修改视图的安全区域等内容。