Airbnb学习总结

轮播图 滚动视图 跳转 切换显示的页面 使用枚举类型管理状态 样式封装复用

1.如何写出一个轮播图

  • 使用Tabview来实现功能,给它添加一个tabViewStyle .page,然后就会以页的形式来呈现

    var body: some View {
            //image
            TabView{
                code
            }
            .tabViewStyle(.page)
    }
    
  • 添加图片集

    var images = [
            "listing-1",
            "listing-2",
            "listing-3",
            "listing-4"
    ]
    
  • 在tabView中使用ForEach遍历所有图片

    ForEach(images,id:\.self) { image in
    	//这里写的是需要构建的结构,信息是从上一行传过来的
    	Image(image)
    			.resizable()
    			.aspectRatio(contentMode: .fill)
    }
    
  • 如果使用动态编码,就需要使用一个常量:后面写数据的类型,比如Listing
  • 但是下面的预览中,我们需要给view传入实际的可以使用的Listing对象,需要有具体的来源

  • 完整代码

    import SwiftUI
    
    // 组件里边只放最必要,并且可以重用的内容
    // 就比如这个carouse组件,实现功能就好,frame和边框圆角用的时候再写
    struct ListingImageCarouseView: View {
    
        let listing:Listing
          
        var body: some View {
            //image
            TabView{
                ForEach(listing.imageUrls,id:\.self){ image in
                    Image(image)
                        .resizable()
                        .aspectRatio(contentMode: .fill)
    
                }
            }
            .tabViewStyle(.page)
        }
    }
    
    #Preview {
        ListingImageCarouseView(listing: DeveloperPreview.shared.listing[0])
    }
    
    

2.使用ScrollView实现ExploreView滚动视图

  • 在ScrollView中添加一个LazyStack,然后在里面使用ForEach循环创建视图

  • 如果使用硬编码,可以直接使用 0 … 10 语法来指定生成的数量

    ScrollView{
       
      LazyVStack(spacing:30){
        ForEach(0 ... 10,id:\.self) { listing in
                 ListingItemView(listing: listing)
                            .frame(height: 330)
                            .clipShape(RoundedRectangle(cornerRadius: 15))      
              }
         }                   
    }
    
    • clipShape用来剪切视图形状,比如圆形Circle,圆角矩形RoundedRectangle,形状的常用属性是圆角半径-cornerRadius。

3.ExploreView中有多个ItemView,如何进行点击跳转

  • 在NavigationStack中实现跳转

  • 给ForEach构建的结构外面加一层NavigationLink,参数value是ForEach中传下的listing

  • 返回上一界面

    • 使用dismiss环境变量

      NavigationStackView、Sheet等产生的次级界面,可使用 @Environment(\.dismiss) var dismiss 自行控制消失。

      //先设置Didmiss环境变量
      @Environment(\.dismiss) var dismiss
      
      //给按钮添加dismiss方法,返回上级视图
      ZStack(alignment:.topLeading){
          ListingImageCarouseView(listing: listing)
              .frame(height: 270)
          Button{
              dismiss()
          }label: {
              Image(systemName: "chevron.left")
                  .foregroundColor(.black)
                  .background{
                      Circle()
                          .fill(.white)
                          .frame(width: 32,height: 32)
                  }
                  //添加这个padding的目的是把返回按钮放到能看见的地方
                  .padding(32)
          }
      }
      

4.如何切换显示的页面(显示不同页面,不是跳转)

  • 创建一个Bool类型的Flag,通过反转这个flag的状态实现页面切换

    @State private var showView = false
    
  • 给目标View设置一个环境,环境里有一个方法,dismiss,调用它可以返回到上一页面

    @Environment(\.dismiss) var dismiss
    
  • 加一个对于flag的判断,并且给需要跳转到另外一个页面的组件添加点击手势(onTapGesture)

    // showView是设置的bool类型的flag
    if showView{
      DestinationView()
      	// 我们需要跳转回来,所以子页面中有环境
      	.environmentObject()
    }else{
      SearchAndFilterBar()
    		.onTapGesture{
          	withAnimation(.snappy){
          		// 切换showView的状态
            	showView.toggle()
          	}
       }
       //剩余的其他UI代码
    }
    

5.如何实现搜索框,边框如何设置

7.如何实现日期选择(DataPicker)和人数选择(Stepper)

8.如何使用枚举类型管理状态

  • 先创建一个枚举类型

    enum DestinationSearchOption{
      case location
      case dates
      case guests
    }
    
  • 将enum作为一个State的property

    //设置状态变量对应的enum,三个状态同一时间只需要打开一个,所以只赋值一个
    @State private var selectedOption:DestinationSearchOption = .location
    
  • 给VStack添加一个属性onTapGesture,说明如果有点击动作,执行什么操作

    // 一共有三个互相切换状态的VStack,每个VStack包含两种呈现效果
    // 状态使用 onTapGesture 配合enum 实现切换,呈现效果则使用if进行判断,有两套不同的代码
    VStack(){
      if selectedOption == .location{
        // 激活状态下呈现的效果代码
      }else{
        // 普通的代码
      }
    }
    .onTapGesture{
      // 添加动画
      withAnimation{
        selectedOption = .location
      }
    }
    
    // 如果有多个VStack,全都添加
    

9.如何将样式封装成一个组件,实现复用(modifier)

样式的封装复用和组件稍有不同,它有一个ViewModifyer协议,并且作为一个View返回

structure ViewModifier:ViewModifier{
  func body(content:Content) -> same View {
    content
    	.padding()
    	.background(.white)
    	.shawod(radius:10)
  }
}

10.如何实现响应的Clear按钮,当有内容时显示

添加一个状态判断,当满足什么条件时,有这个按钮,这里使用的是判断是否为空,同时给按钮加上期望的功能。

if !viewModel.searchLocation.isEmpty{
      Button("Clear"){
      // clear button
      viewModel.searchLocation = ""
      }
      .foregroundStyle(.black)
      .font(.subheadline)
      .fontWeight(.semibold)
      .padding()
}

11.TabView的实现与布局

  • 最外层是一个TabView组件,里面可以放多个View视图,属性 .tabItem是最下面每个View显示出来的内容,一般是一个Text和一个Image图标,我们可以使用一个Label同时添加这两个组件

    Label("Explore",systemName:"magnifyingglass")
    

12.listDetailView页面的细节实现

  • ### 轮播图忽略safeArea

    ####     属性:.ignoresSafeArea( )
    
  • ### 如何实现Detail中part4中的横向滑动效果

    ####   主要有三点:
    
    - #### 使用ScrollView
    - #### 横向摆放:给ScrollView添加属性 .horizental
    - #### .ScrollTargetBehavior(.paging)
      - #### .paging的作用是让ScrollView中的page完整实现,会有弹跳效果,如果不加,可以拖动到任意一个位置
    
  • ### scrollTargetBehavior

13.如何添加地图

Boss Question : 如何实现动态编码

## 目录结构

## 	![](/Volumes/countstarss/iOS-lupin/截屏2024-04-09 08.43.15.png)

14.如何使用外部数据设置地图默认坐标

15.实现city和state的搜索功能

tip1: 只要在struct中声明了常量,那么在引用这个View的时候就需要把这个作为参数传入

Written on April 9, 2024