Section 09 : Color Literal, Gradient and Overlay

本文详细介绍了SwiftUI中的颜色字面量、渐变效果(包括线性、角度和径向渐变)及覆盖(overlay)修饰符的使用方法。通过实例演示如何在视图中应用这些效果,并探讨了有序使用修饰符的重要性。

Section 09 : Color Literal, Gradient and Overlay - Color Literal,渐变和覆盖(11’54")

Quickly edit colors and add gradients and use the overlay modifier.

快速编辑颜色和渐变与覆盖(overlay)修饰器的使用

1. 渐变

(1)基本渐变

首先看一下 background 修饰,其参数是 View,也就是说任何 View 都可以成为背景。所以我们在指定背景颜色的时候,不能单纯写 background(.white),而应该写成 background(Color.white),因为 Color 才属于视图,而具体的颜色名字则不是。同样,渐变 gradient 也是一种 View 。可以用下面的方法生成一个基本渐变。

// 颜色数组,第一个元素是开始颜色,最后一个元素是结束颜色,中间的是过渡色
Gradient(colors:[.red, .yellow, .blue]		

(2)渐变方式

在 SWiftUI 中,渐变有三种形式:

  • Linear Gradient - 线性渐变,从起点到终点沿着某个直线方向进行渐变。生成方法:
LinearGradient(gradient: Gradient, 			// 基本渐变
               startPoint: UnitPoint, 		// 起点位置
               endPoint: UnitPoint)			// 终点位置
  • Angular Gradient - 角度渐变,从起点到终点沿着顺时针方向渐变,角度取值为 0~2π。有两种创建方式:
// 指明基本渐变 gradient,旋转角的顶点 center,旋转开始的偏移角度(顺时针为正,默认 0 是x轴正方向)
AngularGradient(gradient: Gradient, center: UnitPoint, angle: Angle = .zero)
// 第二种方法
AngularGradient(gradient: Gradient, 		// 基本渐变
                center: UnitPoint, 			// 角的顶点位置
                startAngle: Angle = .zero, 	// 起始偏移角度,默认值为 0
                endAngle: Angle = .zero)	// 结束偏移角度,默认值为 0
  • 径向渐变,即以某点为圆心,以圆心为起点,沿着半径以同心圆的方式逐步向外渐变。创建方式:
RadialGradient(gradient: Gradient, 		// 基本渐变
               center: UnitPoint, 		// 圆心位置
               startRadius: CGFloat, 	// 起始颜色半径
               endRadius: CGFloat)		// 结束颜色半径

这里提到的位置,是按照形状的 8 个控制点。画个参考(希望大家能明白我的意思,呵呵)

这个表头不知道怎么去掉
topLeadingtoptopTrailing
leadingcentertrailing
bottomLeadingbottombottomTrailing

2. Color Literal

color literal 不知道怎么翻译,literal 我记得是字面量的意思,但是这里。。。大家说说看

一般情况下,我们在处理颜色的时候,可能会选择系统内置的名称,或者使用颜色的初始化器,像下面这样:

Color.blue		// 使用内置名称
Color(red: Double, green: Double, blue: Double)		// 使用 RGB 参数

Xcode 还给我们提供了一种便利的方式来处理颜色。当创建颜色时,在Color()的括号中输入 colorl,会在自动完成的菜单中看到 Color Literal的选项,选择后键入,将会在括号内出现一个色块,默认是白色(或黑色?)。双击这个色块会弹出色彩选择窗口。可以在窗口中选择已有的颜色,还可以单击 other... 按钮去打开 Colors 窗口,其中提供更多的处理选择。这样我们始终在编辑器中可以看见最终颜色的结果,还是很方便的。

3. 自定义头像(Avatar……阿凡达? 😂)

最初的思路肯定是在 VStack 中加上头像的图片,然后使用偏移修饰,像下面这样:

VStack(spacing: 16) {
                Image("wowLM_small")					// 头像图片
                    .resizable()						// 可变大小
                    .frame(width: 80, height: 80)		// 定义大小
                    .offset(y: -60)						// 偏移
                MenuRow(title: "账  号", iconName: "gear")
                MenuRow(title: "账  单", iconName: "creditcard")
                MenuRow(title: "退  出", iconName: "person.circle")
                
            }
            .frame(maxWidth: .infinity)
            .frame(height: 300)
            .background(                                                               // 线性渐变
                LinearGradient(gradient: Gradient(colors: [Color(#colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)), Color(#colorLiteral(red: 0.8705882353, green: 0.8941176471, blue: 0.9450980392, alpha: 1))]),     // 基础渐变,起始颜色和结束颜色
                               startPoint: .top,                                       // 起始位置
                               endPoint: .bottom))                                     // 结束位置
            .clipShape(RoundedRectangle(cornerRadius: 30, style: .continuous))
            .shadow(radius: 30)
            .padding()

这样做的结果是图片被裁剪了,因为整个 VStack 有个 clipShape 修饰。所以这么不行。我们要采用 overlay 的方式来回避裁剪,将头像图片完整地显示出来。注释掉前面的 Image 和 修饰符之后,在 VStack 的 clipShape 修饰后面加上 overlay 修饰,

 .overlay(
     Image("wowLM_small")                    // 头像图片
         .resizable()                        // 可变大小
         .aspectRatio(contentMode: .fill)    // 填充宽高比例
         .frame(width: 60, height: 60)       // 定义大小
         .clipShape(Circle())                // 裁剪
         .offset(y: -150)                    // 偏移
)

4. 添加文字说明和进度条

(1)在 VStack 中的菜单项目之前增加一个关于进度的 Text。

Text("Howard Ge - 完成度 52%")
	.font(.caption)

(2)在上面的文本下面添加进度条

Color.white
    .frame(width: 38, height: 6)                            // 进度标志
    .cornerRadius(3)                                        // 圆角修饰
    .frame(width: 130, height: 6, alignment: .leading)      // 放入新的框架
    .background(Color.black.opacity(0.08))
    .cornerRadius(3)
    .padding()
    .frame(width: 150, height: 24)                          // 再放入新的框架
    .background(Color.black.opacity(0.1))
    .cornerRadius(12)

这里我们看到依旧是从里向外的逐步实现,先定义了进度条上的浮标,然后使用 frame 修饰扩展到进度条,然后再用 frame 修饰将整个进度条放入一个新的框架中。由此我们得到以下结论:

  • 结论 1 :我们再次体会到了修饰器(modifier)的使用是有顺序的。
  • 结论 2 :修饰器总是修饰之前已经形成的结果。
  • 结论 2 :上面的结论决定了 SWiftUI 实现组件的过程是从细节到全局的顺序。

5. 调整 VStack 的阴影修饰

通常情况下,原有的阴影修饰方式简便易用。这里介绍一种功能更加完善的方式:

.shadow(color: Color.black.opacity(0.2), radius: 20, x: 0, y: 20)       // 调整了shadow

其中不仅可以设置阴影的颜色、半径,还可以设置阴影的偏移,使其显示更加灵活多变。

6. 修改 MenuRow

刚才介绍了 Color Literal 方式来选择颜色,现在到 MenuRow 视图中练习一下。修改 SF 符号的颜色,在 Image 组件的修饰后面添加颜色修饰。

.foregroundColor(Color(#colorLiteral(red: 0.662745098, green: 0.7333333333, blue: 0.831372549, alpha: 1)))                     // 使用 Color Literal 选择颜色

😲 wow~~~呵呵,这可能和你在 Xcode 中看到的不一样。没事的,因为我只是复制粘贴过来,所以解析前的完整代码就显示出来了,而在 Xcode 的编辑器里,是会被解析的,你会在括号里面看到一个灰色的色块。

BTW,那个色块的十六进制编码是 #A9BBD4

本节小结

本节代码请参见 GitHub码云

  • 基本渐变的参数由 Color 数组构成。
  • 渐变的形式有三种:线性渐变(LinearGradient),角度渐变(AngularGradient)和径向渐变(RadiantGradient)。
  • 渐变的设置需要有:基本渐变,一个或多个位置,起始和结束的参数。
  • 位置是组件的 8 个……控制柄?(PS 还是 Windows 好像这么说的)
  • 输入颜色时,可以利用 Color Literal 的自动完成,双击色块打开窗口进行选择或更多处理。
  • overlay 修饰可以避开 clipShape 的裁剪,使图片或其他视图完整的显示在现有视图之上。
  • 有序使用修饰,完成从细节到全局的组件实现。
  • shadow 修饰的另一种更全面的用法。
接下来

下一节将讨论屏幕之间的动画……加油!

格式转换错误 [17ww+0]: invalid literal for int() with base 10: '17w' 格式转换错误 [12ww+1]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+1]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+1]: invalid literal for int() with base 10: '14w' 格式转换错误 [17ww+1]: invalid literal for int() with base 10: '17w' 格式转换错误 [12ww+0]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+0]: invalid literal for int() with base 10: '13w' 格式转换错误 [15ww+0]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+0]: invalid literal for int() with base 10: '16w' 格式转换错误 [11ww+1]: invalid literal for int() with base 10: '11w' 格式转换错误 [12ww+1]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+1]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+1]: invalid literal for int() with base 10: '14w' 格式转换错误 [11ww+0]: invalid literal for int() with base 10: '11w' 格式转换错误 [12ww+0]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+0]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+0]: invalid literal for int() with base 10: '14w' 格式转换错误 [13ww+1]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+1]: invalid literal for int() with base 10: '14w' 格式转换错误 [16ww+1]: invalid literal for int() with base 10: '16w' 格式转换错误 [17ww+1]: invalid literal for int() with base 10: '17w' 格式转换错误 [11ww+6]: invalid literal for int() with base 10: '11w' 格式转换错误 [13ww+6]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+6]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+6]: invalid literal for int() with base 10: '15w' 格式转换错误 [12ww+4]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+4]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+4]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+4]: invalid literal for int() with base 10: '15w' 格式转换错误 [12ww+1]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+1]: invalid literal for int() with base 10: '13w' 格式转换错误 [15ww+1]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+1]: invalid literal for int() with base 10: '16w' 格式转换错误 [11ww+2]: invalid literal for int() with base 10: '11w' 格式转换错误 [12ww+2]: invalid literal for int() with base 10: '12w' 格式转换错误 [15ww+2]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+2]: invalid literal for int() with base 10: '16w' 格式转换错误 [12ww+4]: invalid literal for int() with base 10: '12w' 格式转换错误 [14ww+4]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+4]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+4]: invalid literal for int() with base 10: '16w' 格式转换错误 [12ww+1]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+1]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+1]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+1]: invalid literal for int() with base 10: '15w' 格式转换错误 [12ww+6]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+6]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+6]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+6]: invalid literal for int() with base 10: '15w' 格式转换错误 [13ww+1]: invalid literal for int() with base 10: '13w' 格式转换错误 [15ww+1]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+1]: invalid literal for int() with base 10: '16w' 格式转换错误 [17ww+1]: invalid literal for int() with base 10: '17w' 格式转换错误 [12ww+2]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+2]: invalid literal for int() with base 10: '13w' 格式转换错误 [15ww+2]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+2]: invalid literal for int() with base 10: '16w' 格式转换错误 [13ww+5]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+5]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+5]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+5]: invalid literal for int() with base 10: '16w' 格式转换错误 [13ww+6]: invalid literal for int() with base 10: '13w' 格式转换错误 [16ww+6]: invalid literal for int() with base 10: '16w' 格式转换错误 [19ww+6]: invalid literal for int() with base 10: '19w' 格式转换错误 [20ww+6]: invalid literal for int() with base 10: '20w' 格式转换错误 [12ww+2]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+2]: invalid literal for int() with base 10: '13w' 格式转换错误 [16ww+2]: invalid literal for int() with base 10: '16w' 格式转换错误 [17ww+2]: invalid literal for int() with base 10: '17w' 格式转换错误 [12ww+4]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+4]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+4]: invalid literal for int() with base 10: '14w' 格式转换错误 [17ww+4]: invalid literal for int() with base 10: '17w' 格式转换错误 [13ww+5]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+5]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+5]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+5]: invalid literal for int() with base 10: '16w' 格式转换错误 [12ww+1]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+1]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+1]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+1]: invalid literal for int() with base 10: '15w' 格式转换错误 [13ww+4]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+4]: invalid literal for int() with base 10: '14w' 格式转换错误 [16ww+4]: invalid literal for int() with base 10: '16w' 格式转换错误 [17ww+4]: invalid literal for int() with base 10: '17w' 格式转换错误 [13ww+4]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+4]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+4]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+4]: invalid literal for int() with base 10: '16w' 格式转换错误 [12ww+5]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+5]: invalid literal for int() with base 10: '13w' 格式转换错误 [15ww+5]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+5]: invalid literal for int() with base 10: '16w' 格式转换错误 [11ww+1]: invalid literal for int() with base 10: '11w' 格式转换错误 [12ww+1]: invalid literal for int() with base 10: '12w' 格式转换错误 [15ww+1]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+1]: invalid literal for int() with base 10: '16w' 格式转换错误 [13ww+1]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+1]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+1]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+1]: invalid literal for int() with base 10: '16w' 格式转换错误 [12ww+1]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+1]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+1]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+1]: invalid literal for int() with base 10: '15w' 格式转换错误 [13ww+0]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+0]: invalid literal for int() with base 10: '14w' 格式转换错误 [16ww+0]: invalid literal for int() with base 10: '16w' 格式转换错误 [18ww+0]: invalid literal for int() with base 10: '18w' 格式转换错误 [12ww+5]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+5]: invalid literal for int() with base 10: '13w' 格式转换错误 [15ww+5]: invalid literal for int() with base 10: '15w' 格式转换错误 [17ww+5]: invalid literal for int() with base 10: '17w' 格式转换错误 [13ww+3]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+3]: invalid literal for int() with base 10: '14w' 格式转换错误 [16ww+3]: invalid literal for int() with base 10: '16w' 格式转换错误 [17ww+3]: invalid literal for int() with base 10: '17w' 格式转换错误 [13ww+5]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+5]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+5]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+5]: invalid literal for int() with base 10: '16w' 格式转换错误 [11ww+1]: invalid literal for int() with base 10: '11w' 格式转换错误 [12ww+1]: invalid literal for int() with base 10: '12w' 格式转换错误 [14ww+1]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+1]: invalid literal for int() with base 10: '15w' 格式转换错误 [13ww+2]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+2]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+2]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+2]: invalid literal for int() with base 10: '16w' 格式转换错误 [11ww+5]: invalid literal for int() with base 10: '11w' 格式转换错误 [13ww+5]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+5]: invalid literal for int() with base 10: '14w' 格式转换错误 [16ww+5]: invalid literal for int() with base 10: '16w' 格式转换错误 [12ww+5]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+5]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+5]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+5]: invalid literal for int() with base 10: '15w' 格式转换错误 [12ww+4]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+4]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+4]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+4]: invalid literal for int() with base 10: '15w' 格式转换错误 [13ww+2]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+2]: invalid literal for int() with base 10: '14w' 格式转换错误 [17ww+2]: invalid literal for int() with base 10: '17w' 格式转换错误 [18ww+2]: invalid literal for int() with base 10: '18w' 格式转换错误 [12ww+3]: invalid literal for int() with base 10: '12w' 格式转换错误 [14ww+3]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+3]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+3]: invalid literal for int() with base 10: '16w' 格式转换错误 [12ww+2]: invalid literal for int() with base 10: '12w' 格式转换错误 [14ww+2]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+2]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+2]: invalid literal for int() with base 10: '16w' 格式转换错误 [13ww+2]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+2]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+2]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+2]: invalid literal for int() with base 10: '16w' 格式转换错误 [13ww+1]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+1]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+1]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+1]: invalid literal for int() with base 10: '16w' 格式转换错误 [11ww+6]: invalid literal for int() with base 10: '11w' 格式转换错误 [12ww+6]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+6]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+6]: invalid literal for int() with base 10: '14w' 格式转换错误 [13ww+3]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+3]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+3]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+3]: invalid literal for int() with base 10: '16w' 格式转换错误 [13ww+0]: invalid literal for int() with base 10: '13w' 格式转换错误 [15ww+0]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+0]: invalid literal for int() with base 10: '16w' 格式转换错误 [17ww+0]: invalid literal for int() with base 10: '17w' 格式转换错误 [13ww+2]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+2]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+2]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+2]: invalid literal for int() with base 10: '16w' 格式转换错误 [12ww+4]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+4]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+4]: invalid literal for int() with base 10: '14w' 格式转换错误 [16ww+4]: invalid literal for int() with base 10: '16w' 格式转换错误 [13ww+3]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+3]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+3]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+3]: invalid literal for int() with base 10: '16w' 格式转换错误 [11ww+4]: invalid literal for int() with base 10: '11w' 格式转换错误 [12ww+4]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+4]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+4]: invalid literal for int() with base 10: '14w' 格式转换错误 [11ww+5]: invalid literal for int() with base 10: '11w' 格式转换错误 [13ww+5]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+5]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+5]: invalid literal for int() with base 10: '15w' 格式转换错误 [13ww+5]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+5]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+5]: invalid literal for int() with base 10: '15w' 格式转换错误 [18ww+5]: invalid literal for int() with base 10: '18w' 格式转换错误 [12ww+1]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+1]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+1]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+1]: invalid literal for int() with base 10: '15w' 格式转换错误 [12ww+4]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+4]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+4]: invalid literal for int() with base 10: '14w' 格式转换错误 [15ww+4]: invalid literal for int() with base 10: '15w' 格式转换错误 [12ww+2]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+2]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+2]: invalid literal for int() with base 10: '14w' 格式转换错误 [16ww+2]: invalid literal for int() with base 10: '16w' 格式转换错误 [12ww+4]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+4]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+4]: invalid literal for int() with base 10: '14w' 格式转换错误 [17ww+4]: invalid literal for int() with base 10: '17w' 格式转换错误 [11ww+5]: invalid literal for int() with base 10: '11w' 格式转换错误 [12ww+5]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+5]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+5]: invalid literal for int() with base 10: '14w' 格式转换错误 [11ww+5]: invalid literal for int() with base 10: '11w' 格式转换错误 [12ww+5]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+5]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+5]: invalid literal for int() with base 10: '14w' 格式转换错误 [13ww+5]: invalid literal for int() with base 10: '13w' 格式转换错误 [15ww+5]: invalid literal for int() with base 10: '15w' 格式转换错误 [16ww+5]: invalid literal for int() with base 10: '16w' 格式转换错误 [17ww+5]: invalid literal for int() with base 10: '17w' 格式转换错误 [11ww+4]: invalid literal for int() with base 10: '11w' 格式转换错误 [12ww+4]: invalid literal for int() with base 10: '12w' 格式转换错误 [13ww+4]: invalid literal for int() with base 10: '13w' 格式转换错误 [14ww+4]: invalid literal for int() with base 10: '14w' Traceback (most recent call last): File "C:\Users\1\PycharmProjects\pythonProject1\study_1.py", line 60, in <module> raise ValueError(f"缺少必要列: {missing_cols}") ValueError: 缺少必要列: ['BMI']
最新发布
10-21
<think>好的,我现在需要帮用户解决他们遇到的问题。用户之前提供了Python代码,并遇到了两个主要问题:一个是孕周格式转换错误,另一个是缺少必要列'BMI'的错误。我需要仔细分析这两个问题,并提供相应的解决方案。 首先,关于孕周格式转换错误。用户提供的错误信息显示,输入的孕周格式中有类似'17ww+0'这样的字符串,代码在尝试转换时失败了。原代码中的convert_gestational_age函数可能没有正确处理这种重复的'ww'情况。我需要检查这个函数,并调整逻辑,使其能够处理多余的'w'字符。可能的解决方案是使用正则表达式来提取周数和天数,或者替换掉多余的'w'字符,确保正确分割周数和天数部分。 然后,第二个错误是缺少'BMI'列。用户的数据中可能存在列名不一致的问题,比如列名是' BMI'(带有空格)或者其他变体。这时候需要检查数据中的实际列名,并在代码中进行相应的处理,比如去除空格或重命名列。此外,用户可能需要在代码中添加列存在性检查,确保所需列确实存在,避免后续处理出错。 接下来,我需要综合这两个问题,给出一个完整的代码修改方案。首先,修改convert_gestational_age函数,使其更健壮,能够处理各种格式的输入,包括多余的'w'字符。然后,添加列存在性检查,确保'BMI'、'Y染色体浓度'等必要列存在,如果不存在,给出提示或进行修正。同时,可能需要优化错误处理,让用户更容易定位和解决问题。 另外,用户可能需要一些额外的建议,比如如何验证数据中的列名,如何处理可能的空值,或者如何记录日志以便后续调试。这些都能帮助用户更好地维护和运行代码。 最后,我需要确保回答符合用户的要求,即在回答末尾列出相关问题,并且格式正确。相关问题应该围绕当前的问题,比如如何处理其他列名错误、如何验证转换后的数据准确性、如何处理空值等,帮助用户进一步扩展知识。</think>--- ### **问题分析** 1. **孕周格式错误** 输入中存在 `17ww+0` 这类格式,原始代码未处理重复的 `w` 字符。 2. **列缺失错误** 报错 `缺少必要列: ['BMI']` 表明数据中实际列名可能与代码要求的列名不一致(如含空格、大小写差异等)。 --- ### **解决方案** #### 1. 修正孕周格式转换函数(增强容错性) ```python import re def convert_gestational_age(age_str): """ 支持处理重复w、大小写、空格等异常格式 示例输入: "17ww+0" → 119天, "15周+2天" → 107天 """ try: # 统一格式:去除多余字符 age_str = age_str.lower().replace(" ", "").replace("周", "w").replace("天", "") # 使用正则提取周数和天数 match = re.match(r'(\d+)w\+?(\d*)', age_str) if not match: raise ValueError(f"无法解析格式: {age_str}") weeks = int(match.group(1)) days = int(match.group(2)) if match.group(2) else 0 return weeks * 7 + days except Exception as e: print(f"格式转换错误 [{age_str}]: {str(e)}") return float('nan') ``` #### 2. 检查并修正列名(自动对齐格式) ```python # 读取数据后添加列名检查与修正 df.columns = [col.strip() for col in df.columns] # 去除列名前后空格 required_columns = ["孕妇代码", "BMI", "Y染色体浓度", "检测孕周"] # 检查缺失列并尝试模糊匹配 missing_cols = [col for col in required_columns if col not in df.columns] if missing_cols: print(f"警告:以下列缺失,尝试模糊匹配...") for req_col in required_columns: matched = [col for col in df.columns if req_col.lower() in col.lower()] if matched: print(f"自动匹配 '{req_col}' → '{matched[0]}'") df.rename(columns={matched[0]: req_col}, inplace=True) else: raise ValueError(f"无法找到匹配列: {req_col}") ``` --- ### **完整修正代码** ```python import pandas as pd import os import re # 文件路径定义 INPUT_FILE = r"C:\Users\1\Desktop\附件.xlsx" OUTPUT_FILE = r"C:\Users\1\Desktop\处理后结果.xlsx" # 文件存在性检查 if not os.path.exists(INPUT_FILE): raise FileNotFoundError(f"文件未找到: {INPUT_FILE}") # 读取文件 try: df = pd.read_excel(INPUT_FILE) print(f"成功读取文件,共加载 {len(df)} 条记录") except Exception as e: print(f"读取文件时发生错误: {str(e)}") raise # 列名修正(去除空格、大小写匹配) df.columns = [col.strip() for col in df.columns] required_columns = ["孕妇代码", "BMI", "Y染色体浓度", "检测孕周"] missing_cols = [col for col in required_columns if col not in df.columns] if missing_cols: print(f"警告:以下列缺失,尝试模糊匹配...") for req_col in required_columns: matched = [col for col in df.columns if req_col.lower() in col.lower()] if matched: print(f"自动匹配 '{req_col}' → '{matched[0]}'") df.rename(columns={matched[0]: req_col}, inplace=True) else: raise ValueError(f"无法找到匹配列: {req_col}") # 孕周格式转换函数(增强版) def convert_gestational_age(age_str): try: age_str = age_str.lower().replace(" ", "").replace("周", "w").replace("天", "") match = re.match(r'(\d+)w\+?(\d*)', age_str) if not match: raise ValueError(f"无法解析格式: {age_str}") weeks = int(match.group(1)) days = int(match.group(2)) if match.group(2) else 0 return weeks * 7 + days except Exception as e: print(f"格式转换错误 [{age_str}]: {str(e)}") return float('nan') # 数据转换与处理 df["检测孕周_天数"] = df["检测孕周"].apply(convert_gestational_age) df_filtered = df[(df["检测孕周_天数"] >= 70) & (df["检测孕周_天数"] <= 175)] # 按孕妇代码聚合 df_processed = df_filtered.groupby("孕妇代码").agg({ "BMI": "mean", "Y染色体浓度": "mean", "检测孕周_天数": "mean" }).reset_index() # 保存结果 df_processed.to_excel(OUTPUT_FILE, index=False) print(f""" 处理完成! - 原始数据: {len(df)} 条 - 有效数据: {len(df_processed)} 条 - 输出文件已保存至: {OUTPUT_FILE} """) ``` --- ### **关键改进点** | 改进项 | 解决方案 | |----------------|--------------------------------------------------------------------------| | **多重w格式** | 使用正则 `(\d+)w\+?(\d*)` 匹配 `17ww+0` → 提取 `17` 周 `0` 天 | | **列名不一致** | 自动去除空格、大小写匹配(如 `bmi` → `BMI`) | | **异常处理** | 保留错误提示但继续处理有效数据,避免程序中断 | --- ### **验证建议** 1. **检查列名** 在Excel中查看列名是否包含隐藏字符(如全角空格、特殊符号)。 2. **数据示例测试** 对异常格式(如 `17ww+0`、`15周+2天`)单独测试转换函数。 3. **日志输出** 添加日志记录器,将错误信息写入文件以便后续分析。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值