赞
踩
技术:SwiftUI、SwiftUI4.0、分页、轮播列表、页面控件、自定义分段控件、自定义对战控件
运行环境:
SwiftUI4.0 + Xcode14 + MacOS12.6 + iPhone Simulator iPhone 14 Pro Max
使用SwiftUI做一个一个类似
微博
、网易云
、抖音个人页面
的头部下拉放大图片
- 效果
思路:
1.创建头部模块 进行测试上下滚动拥有放大缩小效果
2.搭建分类模块 固定在头部下面
3.搭建列表模块
4.监听滚动偏移的操作
SpotifyResponvieUI
颜色
BG#281A1A
Green#4DD037
随机图片9张
个人大图背景1张
logo1张
New Group
命名为 View
New Group
命名为 Model
New File
选择SwiftUI View
类型 命名为Album
并且继承Identifiable
New File
选择SwiftUI View
类型 命名为Home
主要是:
New File
选择SwiftUI View
类型 命名为OffsetModifier
主要是展示主窗口
Home
和设置暗黑模式
import SwiftUI struct ContentView: View { var body: some View { Home() // 永远是黑暗模式 .preferredColorScheme(.dark) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
思路
- 主要就是展示大图背景 + 固定的分类 + 列表模块
// // Home.swift // SpotifyResponvieUI (iOS) // // Created by lyh on 2022/8/23. // import SwiftUI struct Home: View { @State var currentType: String = "Popular" // 光滑滑动效果 @Namespace var animation @State var _albums: [Album] = albums // x,y @State var headerOffsets: (CGFloat,CGFloat) = (0,0) var body: some View { ScrollView(.vertical, showsIndicators: false) { VStack(spacing: 0){ HeaderView() // 带内容的固定标题 LazyVStack(pinnedViews: [.sectionHeaders]) { Section { SongList() } header: { PinnedHeaderView() .background(Color.black) .offset(y: headerOffsets.1 > 0 ? 0 : -headerOffsets.1 / 8) .modifier(OffsetModifier(offset: $headerOffsets.0, returnFromStart: false)) .modifier(OffsetModifier(offset: $headerOffsets.1)) } } } } .overlay(content: { Rectangle() .fill(.black) .frame(height: 50) .frame(maxHeight: .infinity,alignment: .top) .opacity(headerOffsets.0 < 5 ? 1 : 0) }) .coordinateSpace(name: "SCROLL") .ignoresSafeArea(.container, edges: .vertical) } // 固定的内容 @ViewBuilder func SongList()->some View{ VStack(spacing: 25){ ForEach($_albums){$album in HStack(spacing: 12){ Text("#\(getIndex(album: album) + 1)") .fontWeight(.semibold) .foregroundColor(.gray) Image(album.albumImage) .resizable() .aspectRatio(contentMode: .fill) .frame(width: 55, height: 55) .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous)) VStack(alignment: .leading, spacing: 8) { Text(album.albumName) .fontWeight(.semibold) Label { Text("65,587,909") } icon: { Image(systemName: "beats.headphones") .foregroundColor(.white) } .foregroundColor(.gray) .font(.caption) } .frame(maxWidth: .infinity,alignment: .leading) Button { album.isLiked.toggle() } label: { Image(systemName: album.isLiked ? "suit.heart.fill" : "suit.heart") .font(.title3) .foregroundColor(album.isLiked ? Color("Green") : .white) } Button { } label: { Image(systemName: "ellipsis") .font(.title3) .foregroundColor(.white) } } } } .padding() .padding(.top,25) .padding(.bottom,150) } func getIndex(album: Album)->Int{ return _albums.firstIndex { currentAlbum in return album.id == currentAlbum.id } ?? 0 } // 头部视图 @ViewBuilder func HeaderView()->some View{ GeometryReader{proxy in let minY = proxy.frame(in: .named("SCROLL")).minY let size = proxy.size let height = (size.height + minY) Image("Ariana") .resizable() .aspectRatio(contentMode: .fill) .frame(width: size.width,height: height > 0 ? height : 0,alignment: .top) .overlay(content: { ZStack(alignment: .bottom) { // 调暗文本内容 LinearGradient(colors: [ .clear, .black.opacity(0.8) ], startPoint: .top, endPoint: .bottom) VStack(alignment: .leading, spacing: 12) { Text("宇夜iOS") .font(.callout) .foregroundColor(.gray) HStack(alignment: .bottom, spacing: 10) { Text("Ariana Grande") .font(.title.bold()) Image(systemName: "checkmark.seal.fill") .foregroundColor(.blue) .background{ Circle() .fill(.white) .padding(3) } } Label { Text("Monthly Listeners") .fontWeight(.semibold) .foregroundColor(.white.opacity(0.7)) } icon: { Text("62,354,659") .fontWeight(.semibold) } .font(.caption) } .padding(.horizontal) .padding(.bottom,25) .frame(maxWidth: .infinity,alignment: .leading) } }) .cornerRadius(15) .offset(y: -minY) } .frame(height: 250) } // 固定在头部 @ViewBuilder func PinnedHeaderView()->some View{ let types: [String] = ["Popular","Albums","Songs","Fans also like","About"] ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 25){ ForEach(types,id: \.self){type in VStack(spacing: 12){ Text(type) .fontWeight(.semibold) .foregroundColor(currentType == type ? .white : .gray) ZStack{ if currentType == type{ RoundedRectangle(cornerRadius: 4, style: .continuous) .fill(.white) .matchedGeometryEffect(id: "TAB", in: animation) } else{ RoundedRectangle(cornerRadius: 4, style: .continuous) .fill(.clear) } } .padding(.horizontal,8) .frame(height: 4) } .contentShape(Rectangle()) .onTapGesture { withAnimation(.easeInOut){ currentType = type } } } } .padding(.horizontal) .padding(.top,25) .padding(.bottom,5) } } } struct Home_Previews: PreviewProvider { static var previews: some View { ContentView() } }
主要是监听ScrollView的滚动
用来监听ScrollView的滚动 偏移量的改变
// // OffsetModifier.swift // SpotifyResponvieUI (iOS) // // Created by lyh on 2022/8/23. // import SwiftUI // 继承于 ViewModifier 最主要是能方便扩展一些常见的设置属性 /* 比如 给Text设置字体\背景颜色\阴影效果 extension Text { func songStyle() -> some View { self .font(.system(size: 24, weight: .bold)) .foregroundColor(.white) .shadow(radius: 20) } } ⭐️如果是继承ViewModifier struct SongTextViewModifier: ViewModifier { func body(content: Content) -> some View { content .font(.system(size: 24, weight: .bold)) .foregroundColor(.white) .shadow(radius: 20) } } 然后直接通过 Text(song) .modifier(SongTextViewModifier()) 设置 */ struct OffsetModifier: ViewModifier { @Binding var offset: CGFloat // 可选从0返回值 var returnFromStart: Bool = true @State var startValue: CGFloat = 0 func body(content: Content) -> some View { content .overlay { GeometryReader{proxy in Color.clear .preference(key: OffsetKey.self, value: proxy.frame(in: .named("SCROLL")).minY) .onPreferenceChange(OffsetKey.self) { value in if startValue == 0{ startValue = value } print(value); offset = (value - (returnFromStart ? startValue : 0)) print("offset is \(offset)"); } } } } } // 偏好的关键 struct OffsetKey: PreferenceKey{ static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } }
// // Album.swift // SpotifyResponvieUI (iOS) // // Created by lyh on 2022/8/23. // import SwiftUI // Ablum模型和样本数据 struct Album: Identifiable { var id = UUID().uuidString var albumName: String var albumImage : String var isLiked : Bool = false } var albums : [Album] = [ Album(albumName: "Positions", albumImage: "Album1"), Album(albumName: "The Best", albumImage: "Album2",isLiked: true), Album(albumName: "My Everything", albumImage: "Album3"), Album(albumName: "Yours Truly", albumImage: "Album4"), Album(albumName: "Sweetener", albumImage: "Album5",isLiked: true), Album(albumName: "Rain On Me", albumImage: "Album6"), Album(albumName: "Stuck With U", albumImage: "Album7"), Album(albumName: "7 rings", albumImage: "Album8",isLiked: true), Album(albumName: "Bang Bang", albumImage: "Album9"), ]
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。