如何将自定义形状定位在按钮的中心
How to position custom shape in a center of a button
我找到了动画自定义标签栏教程并尝试重现它。但是现在我陷入了每次按下按钮时都需要将自定义形状的中心放在按钮中心的时刻。如果有人能向我解释为什么 midX 位于按钮之间,我将不胜感激?
这是预期的结果:
这是我用下面的代码点击按钮时得到的结果:
代码:
struct TabBar: View{
@State private var selectedTab: Tabs.RawValue = Tabs.home.rawValue
@State private var xAxisForCurve: CGFloat = 80
let screenWidth: CGFloat
private var tabs : [String]{
var _tabs: [String] = []
Tabs.allCases.forEach { tab in
_tabs.append(tab.rawValue)
}
return _tabs
}
var body: some View{
HStack{
ForEach(tabs, id: \.self) { tab in
GeometryReader { button in
Button(action: {
withAnimation(.spring()){
self.selectedTab = tab
self.xAxisForCurve = button.frame(in: .global).midX
}
}) {
Image(systemName: tab)
.resizable()
.renderingMode(.template)
.aspectRatio(contentMode: .fit)
.foregroundColor(self.selectedTab == tab ? Color.blue : Color.gray)
}
}
.frame(width: 28, height: 28)
if tab != tabs.last{
Spacer().frame(width: 65)
}
}
}
.frame(width: screenWidth * multiplayerForTabFrameOnDifferentDevices())
.padding(.vertical)
.background(Color.white.clipShape(MyShape(xAxis: self.xAxisForCurve)))
.cornerRadius(20)
}
}
struct MyShape: Shape{
var xAxis: CGFloat
func path(in rect: CGRect) -> Path {
Path{ path in
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: rect.width, y: 0))
path.addLine(to: CGPoint(x: rect.width, y: rect.height))
path.addLine(to: CGPoint(x: 0, y: rect.height))
let upperLeftCorner = CGPoint(x: self.xAxis - 50 , y: rect.minY)
let upperRightCorner = CGPoint(x: self.xAxis + 50, y: rect.minY)
let curveCenter = CGPoint(x: self.xAxis, y: rect.height * 0.55)
let control1 = CGPoint(x: self.xAxis - 25 , y: 0)
let control2 = CGPoint(x: self.xAxis - 25 , y: 35)
let control4 = CGPoint(x: self.xAxis + 25 , y: 0)
let control3 = CGPoint(x: self.xAxis + 25 , y: 35)
path.move(to: upperLeftCorner)
path.addCurve(to: curveCenter, control1: control1, control2: control2)
path.addCurve(to: upperRightCorner, control1: control3, control2: control4)
}
}
}
您正在使用frame(in: .global)
获取全局框架:它是与设备屏幕相关的框架。
但是你的形状需要与你的 TabBar
边界相关的位置。
可以通过添加命名坐标来解决space:
.frame(width: screenWidth * multiplayerForTabFrameOnDifferentDevices())
.coordinateSpace(name: "container")
并在此坐标中获取按钮框架 space:
xAxisForCurve = button.frame(in: .named("container")).midX
p.s。从您的代码看起来您正在尝试为您的形状制作动画。您需要将以下线条添加到形状中以使其可动画化。可以找到更多信息 here
var animatableData: CGFloat {
get { xAxis }
set { xAxis = newValue }
}
我找到了动画自定义标签栏教程并尝试重现它。但是现在我陷入了每次按下按钮时都需要将自定义形状的中心放在按钮中心的时刻。如果有人能向我解释为什么 midX 位于按钮之间,我将不胜感激?
这是预期的结果:
这是我用下面的代码点击按钮时得到的结果:
代码:
struct TabBar: View{
@State private var selectedTab: Tabs.RawValue = Tabs.home.rawValue
@State private var xAxisForCurve: CGFloat = 80
let screenWidth: CGFloat
private var tabs : [String]{
var _tabs: [String] = []
Tabs.allCases.forEach { tab in
_tabs.append(tab.rawValue)
}
return _tabs
}
var body: some View{
HStack{
ForEach(tabs, id: \.self) { tab in
GeometryReader { button in
Button(action: {
withAnimation(.spring()){
self.selectedTab = tab
self.xAxisForCurve = button.frame(in: .global).midX
}
}) {
Image(systemName: tab)
.resizable()
.renderingMode(.template)
.aspectRatio(contentMode: .fit)
.foregroundColor(self.selectedTab == tab ? Color.blue : Color.gray)
}
}
.frame(width: 28, height: 28)
if tab != tabs.last{
Spacer().frame(width: 65)
}
}
}
.frame(width: screenWidth * multiplayerForTabFrameOnDifferentDevices())
.padding(.vertical)
.background(Color.white.clipShape(MyShape(xAxis: self.xAxisForCurve)))
.cornerRadius(20)
}
}
struct MyShape: Shape{
var xAxis: CGFloat
func path(in rect: CGRect) -> Path {
Path{ path in
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: rect.width, y: 0))
path.addLine(to: CGPoint(x: rect.width, y: rect.height))
path.addLine(to: CGPoint(x: 0, y: rect.height))
let upperLeftCorner = CGPoint(x: self.xAxis - 50 , y: rect.minY)
let upperRightCorner = CGPoint(x: self.xAxis + 50, y: rect.minY)
let curveCenter = CGPoint(x: self.xAxis, y: rect.height * 0.55)
let control1 = CGPoint(x: self.xAxis - 25 , y: 0)
let control2 = CGPoint(x: self.xAxis - 25 , y: 35)
let control4 = CGPoint(x: self.xAxis + 25 , y: 0)
let control3 = CGPoint(x: self.xAxis + 25 , y: 35)
path.move(to: upperLeftCorner)
path.addCurve(to: curveCenter, control1: control1, control2: control2)
path.addCurve(to: upperRightCorner, control1: control3, control2: control4)
}
}
}
您正在使用frame(in: .global)
获取全局框架:它是与设备屏幕相关的框架。
但是你的形状需要与你的 TabBar
边界相关的位置。
可以通过添加命名坐标来解决space:
.frame(width: screenWidth * multiplayerForTabFrameOnDifferentDevices())
.coordinateSpace(name: "container")
并在此坐标中获取按钮框架 space:
xAxisForCurve = button.frame(in: .named("container")).midX
p.s。从您的代码看起来您正在尝试为您的形状制作动画。您需要将以下线条添加到形状中以使其可动画化。可以找到更多信息 here
var animatableData: CGFloat {
get { xAxis }
set { xAxis = newValue }
}