iOS小红书Demo开发中,如何实现首页信息流瀑布布局与图片懒加载优化?
我们将使用 SwiftUI 来构建,因为它能让你用更现代、更简洁的代码快速构建出美观的界面。
Demo 核心功能规划
一个精简版的小红书 Demo 应该包含以下功能模块:
- 主页: 无限滚动的“发现”流,包含图文卡片。
- 发布: 一个简化的笔记发布界面,可以输入标题、内容,并选择图片。
- 个人中心: 展示用户信息、发布的笔记列表。
- 底部导航栏: 用于切换主页、发布和个人中心。
第 0 步:项目设置
- 打开 Xcode,选择 "Create a new Xcode project"。
- 选择 iOS -> App,然后点击 Next。
- 填写产品名称("LittleRedBook"),确保 Interface 选择 SwiftUI,Life Cycle 选择 SwiftUI App。
- 点击 Next,选择一个位置保存项目。
第 1 步:数据模型
我们需要定义笔记和用户的数据结构,在 Xcode 中,创建一个新的 Swift File,命名为 Models.swift。
// Models.swift
import Foundation
// 用户模型
struct User: Identifiable, Hashable {
let id = UUID()
let username: String
let avatar: String // 这里用字符串代表图片URL或资源名
}
// 笔记模型
struct Note: Identifiable, Hashable {
let id = UUID()
let user: User
let title: String
let content: String
let imageUrl: String // 用字符串代表图片URL或资源名
let likes: Int
let comments: Int
}
第 2 步:创建假数据
为了不依赖网络,我们先创建一些模拟数据,在 Models.swift 文件末尾添加:
// ... 在 Models.swift 文件末尾添加 ...
// 创建一些模拟用户
let mockUsers: [User] = [
User(username: "美食探索家", avatar: "user1"),
User(username: "旅行日记", avatar: "user2"),
User(username: "穿搭灵感库", avatar: "user3"),
User(username: "摄影小王子", avatar: "user4")
]
// 创建一些模拟笔记
let mockNotes: [Note] = [
Note(
user: mockUsers[0],
title: "周末探店 | 这家日料店也太好吃了吧!",
content: "偶然发现的一家宝藏日料店,三文鱼新鲜到入口即化,环境也很棒,适合约会。",
imageUrl: "note1",
likes: 258,
comments: 42
),
Note(
user: mockUsers[1],
title: "大理古城的浪漫日落",
content: "在古城墙上拍到的日落,整个天空都被染成了橘子汽水的颜色。",
imageUrl: "note2",
likes: 1890,
comments: 156
),
Note(
user: mockUsers[2],
title: "OOTD | 温柔风的秋天穿搭",
content: "米色针织衫 + 卡其色风衣,温柔又高级,姐妹们秋天可以试试这个搭配!",
imageUrl: "note3",
likes: 520,
comments: 88
),
Note(
user: mockUsers[3],
title: "iPhone 摄影技巧 | 如何拍出电影感",
content: "分享3个我用iPhone拍电影感照片的小技巧,快拿去试试!",
imageUrl: "note4",
likes: 1200,
comments: 201
)
// 可以继续添加更多模拟数据...
]
第 3 步:主页 - 无限滚动流
这是小红书最核心的部分,我们将创建一个 HomeView。
1 创建主页视图
在 ContentView.swift 中,我们将它替换为 HomeView。
// Views/HomeView.swift
import SwiftUI
struct HomeView: View {
// 模拟数据
let notes: [Note] = mockNotes
var body: some View {
// 使用ScrollView和LazyVStack来实现高效的无限滚动
ScrollView {
LazyVStack(spacing: 16) {
ForEach(notes) { note in
NoteCard(note: note)
}
}
.padding(.horizontal)
}
.navigationTitle("发现")
.navigationBarTitleDisplayMode(.inline) // 让标题更贴近顶部
}
}
// 笔记卡片组件
struct NoteCard: View {
let note: Note
var body: some View {
VStack(alignment: .leading, spacing: 8) {
// 顶部用户信息
HStack {
Image(note.user.avatar)
.resizable()
.scaledToFill()
.frame(width: 40, height: 40)
.clipShape(Circle())
VStack(alignment: .leading, spacing: 2) {
Text(note.user.username)
.font(.headline)
.fontWeight(.semibold)
Text("刚刚")
.font(.caption)
.foregroundColor(.gray)
}
Spacer()
Button(action: {
// TODO: 实现关注/已关注逻辑
}) {
Text("+ 关注")
.font(.caption)
.fontWeight(.semibold)
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(Color.red)
.foregroundColor(.white)
.clipShape(Capsule())
}
}
// 图片
Image(note.imageUrl)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(height: 300)
.clip(RoundedRectangle(cornerRadius: 12))
// 标题和内容
VStack(alignment: .leading, spacing: 4) {
Text(note.title)
.font(.subheadline)
.fontWeight(.medium)
.multilineTextAlignment(.leading)
Text(note.content)
.font(.caption)
.foregroundColor(.gray)
.multilineTextAlignment(.leading)
}
// 底部互动栏
HStack {
Button(action: {
// TODO: 实现点赞逻辑
}) {
Image(systemName: "heart")
.foregroundColor(.black)
}
Text("\(note.likes)")
.font(.caption)
Spacer()
Button(action: {
// TODO: 跳转到评论页
}) {
Image(systemName: "message")
.foregroundColor(.black)
}
Text("\(note.comments)")
.font(.caption)
Spacer()
Button(action: {
// TODO: 实现分享逻辑
}) {
Image(systemName: "arrowshape.turn.up.right")
.foregroundColor(.black)
}
Spacer()
Button(action: {
// TODO: 实现收藏逻辑
}) {
Image(systemName: "bookmark")
.foregroundColor(.black)
}
}
.padding(.top, 8)
}
.background(Color(.systemBackground))
.clip(RoundedRectangle(cornerRadius: 12))
.shadow(color: Color.black.opacity(0.05), radius: 2, x: 0, y: 1)
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
HomeView()
}
}
}
2 添加图片资源
- 在 Xcode 左侧导航栏,点击
Assets.xcassets。 - 按下
Cmd + N,选择Image Set,命名为note1。 - 将你准备好的图片拖拽到
note1的Universal位置。 - 重复此操作,为
note2,note3,note4和用户头像user1等也添加图片资源。
第 4 步:底部导航栏
现在我们需要一个地方来管理所有视图,创建一个新的 SwiftUI View,命名为 MainTabView.swift。
// Views/MainTabView.swift
import SwiftUI
struct MainTabView: View {
var body: some View {
TabView {
// 主页
NavigationView {
HomeView()
}
.tabItem {
Image(systemName: "house")
Text("首页")
}
// 发布按钮(小红书的特色,放在中间)
AnyView(
Color.clear
.tabItem {
Image(systemName: "plus.circle.fill")
.scaleEffect(1.5)
}
)
// 个人中心
NavigationView {
ProfileView()
}
.tabItem {
Image(systemName: "person")
Text("我的")
}
}
.accentColor(.red) // 设置选中标签的颜色为红色
}
}
// 个人中心视图
struct ProfileView: View {
var body: some View {
VStack {
Text("个人中心")
.font(.largeTitle)
.fontWeight(.bold)
Spacer()
}
}
}
注意: 发布按钮在 TabView 中是一个特殊的设计,上面的代码使用了一个技巧:将中间的 Tab 设置为一个透明的 Color.clear,这样它就不会占据空间,但可以作为一个可点击的区域,在实际开发中,你可能会使用更复杂的自定义 TabBar 来实现这个效果。
第 5 步:发布界面
创建一个新的 SwiftUI View,命名为 CreateNoteView.swift。
// Views/CreateNoteView.swift
import SwiftUI
import PhotosUI // 使用系统照片选择器
struct CreateNoteView: View {
@State private var title = ""
@State private var content = ""
@State private var selectedPhoto: PhotosPickerItem?
@State private var imageData: Data?
var body: some View {
NavigationView {
VStack(spacing: 16) {
// 图片选择区域
ZStack {
if let imageData = imageData, let uiImage = UIImage(data: imageData) {
Image(uiImage: uiImage)
.resizable()
.scaledToFill()
.frame(height: 250)
.clip(RoundedRectangle(cornerRadius: 12))
} else {
RoundedRectangle(cornerRadius: 12)
.fill(Color(.systemGray5))
.frame(height: 250)
.overlay(
VStack {
Image(systemName: "photo.on.rectangle.angled")
.font(.largeTitle)
.foregroundColor(.gray)
Text("选择图片")
.font(.headline)
.foregroundColor(.gray)
}
)
}
}
.overlay(
PhotosPicker(
selection: $selectedPhoto,
matching: .images,
photoLibrary: .shared()
) {
Image(systemName: "camera")
.font(.title)
.foregroundColor(.white)
.padding(12)
.background(Color.black.opacity(0.6))
.clip(Circle())
}
.padding(12),
alignment: .topTrailing
)
// 标题输入
TextField("输入标题", text: $title)
.textFieldStyle(RoundedBorderTextFieldStyle())
.font(.title2)
.fontWeight(.semibold)
// 内容输入
TextEditor(text: $content)
.frame(minHeight: 150)
.overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.gray, lineWidth: 1))
Spacer()
}
.padding()
.navigationTitle("发布笔记")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("取消") {
// 关闭当前视图
// 在实际应用中,这里会有一个 dismiss 动作
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button("发布") {
// TODO: 实现发布逻辑
print("发布笔记: \(title)")
}
.fontWeight(.semibold)
.disabled(title.isEmpty || imageData == nil) // 标题和图片为空时禁用
}
}
.onChange(of: selectedPhoto) { newItem in
Task {
if let data = try? await newItem?.loadTransferable(type: Data.self) {
self.imageData = data
}
}
}
}
}
}
注意: 这里的 PhotosPicker 是 iOS 16 引入的新组件,用于安全地访问用户的相册,如果你需要支持更早的版本,需要使用更旧的 UIImagePickerController。
第 6 步:整合所有视图
修改 YourProjectNameApp.swift 文件,让 MainTabView 作为应用的入口。
// LittleRedBookApp.swift
import SwiftUI
@main
struct LittleRedBookApp: App {
var body: some Scene {
WindowGroup {
MainTabView()
}
}
}
运行和效果
你可以在 Xcode 中按下 Cmd + R 来运行你的 Demo,你应该能看到:
- 一个带有“发现”标题的页面,滚动时可以看到我们创建的笔记卡片。
- 底部有三个标签:首页、一个大的“+”号、我的。
- 点击中间的“+”号,会进入一个空的发布页面(因为我们没有连接导航,所以需要手动修改
MainTabView来跳转)。 - 在发布页面,你可以点击右上角的相机图标选择图片,并输入标题和内容。
总结与下一步
你已经成功创建了一个功能精简但核心元素齐全的小红书 Demo!
可以继续扩展的方向:
- 状态管理: 使用
@State和@Binding管理点赞、收藏等状态。 - 数据持久化: 使用
CoreData或UserDefaults来保存用户的笔记、点赞记录等。 - 网络请求: 集成
URLSession或第三方库(如Alamofire)来从服务器获取真实数据。 - 自定义 TabBar: 实现小红书那种中间凸起的自定义 TabBar,而不是使用系统自带的。
- 详情页: 点击笔记后跳转到详情页,展示大图和完整评论。
- 搜索功能: 在顶部添加一个搜索框。
- 动画效果: 为点赞、关注等操作添加流畅的动画。
这个 Demo 为你提供了一个坚实的基础,你可以基于它继续学习和开发,逐步构建出功能更丰富的应用。
作者:99ANYc3cd6本文地址:https://chumoping.net/post/3060.html发布于 今天
文章转载或复制请以超链接形式并注明出处初梦运营网



