SwiftUI极简教程41:使用Segment、LazyVGrid和ImagePicker构建一个Logo生成器

简介: 在本章中,你将学会使用Segment分段器、LazyVGrid垂直网格、ImagePicker图片选择器构建一个Logo生成器。在上一章中,我们完善了SearchBar搜索栏、TabView底部导航,还有做了一个Loading加载动作。最近突然有个想法,如果把色卡和图片进行组合,这不就是一个简单的Logo了吗?我能不能做个Logo生成器?说干就干,我们继续完成App的相关内容。

image.png

样式部分

Logo展示区域

首先,我们创建一个新的SwiftUI文件,命名为LogoView

然后,我们使用Image作为logo展示的区域,示例:

// MARK: Logo展示区域
private var ShowLogoView: some View {
    Image(systemName: logoImage)
        .font(.system(size: 68))
        .foregroundColor(logoColor)
        .frame(minWidth: 80, maxWidth: 120, minHeight: 80, maxHeight: 120)
        .background(bgColor)
        .cornerRadius(8)
}
复制代码

image.png

上述代码中,我们声明了三个存储变量logoImagelogoColorbgColor,分别可以调整logo图片填充色背景色

在主要展示页面,我们使用Image构建logo视图,并通过修饰符调整logo的图片大小、logo颜色、整体大小背景填充颜色圆角

Segment分段选择

下面我们来实现切换选择调整logo各项参数的区域,我们通过Segment分段选择来区分开,示例:

// MARK: 分选选择
private var SegmentView: some View {
    Picker("分段选择", selection: $selectedSegment) {
        ForEach(0 ..< 3) {
            Text(self.segmentTitle[$0]).tag($0)
       }
    }
    .pickerStyle(SegmentedPickerStyle())
    .padding()
}
复制代码

image.png

上述代码中,我们声明了两个变量segmentTitleselectedSegment

segmentTitle用来存储分段器的标题selectedSegment来记录当前我们选中的是分段器的哪一项。然后我们使用Picker实现了分段器的样式,使用ForEach遍历分段器选项和内容,这里分段器的样式需要选择SegmentedPickerStyle

然后我们在LogoView视图中展示,看起来还不错。

功能交互

背景颜色切换

背景颜色我们使用网格布局来实现,首先我们先声明了网格的列数为4列,然后颜色部分,我们取回之前色卡部分的颜色。

private var gridItemLayout = [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())]
@State var cardItems: [CardModel] = []
复制代码

然后构建背景颜色选择的页面,示例:

//MARK: 背景颜色
private var BGColorView: some View {
    ScrollView {
        LazyVGrid(columns: gridItemLayout, spacing: 20) {
            ForEach(cardItems, id: \.cardColorRBG) { item in
                Rectangle()
                    .fill(Color.Hex(item.cardBGColor))
                    .frame(width: 80, height: 80)
                    .cornerRadius(8)
            }
        }
    }.padding()
}
复制代码

上述代码中,我们构建了一个纵向网格视图,spacing间距为20,然后通过遍历cardItems色卡数组中的数据,然后遍历出一个个色块

我们把网络请求补充上,然后将BGColorView在主视图展示看看效果:

image.png

交互部分,当我们点击背景颜色色块时,上面logo展示区域的logo也同时需要更改背景颜色,我们来实现下。示例:

.onTapGesture {
    bgColor = Color.Hex(item.cardBGColor)
}
复制代码

image.png

我们给背景颜色部分的色卡添加一个点击事件,点击背景颜色色块时,将颜色赋予logo背景颜色。

这样,我们就完成了背景颜色的设置。

图标图片切换

图标部分切换的原理类似,我们这里换成本地的数据做一下演示,先准备一些Apple的系统图标作为示例:

private var appleSymbols = ["house.circle", "person.circle", "bag.circle", "location.circle", "bookmark.circle", "gift.circle", "globe.asia.australia.fill", "lock.circle", "pencil.circle", "link.circle"]
复制代码

然后也通过LazyVGrid纵向网格和ForEach循环的方式遍历appleSymbols图片数组的数据,示例:

// MARK: 图标切换
private var SwitchIconView: some View {
    ScrollView {
        LazyVGrid(columns: gridItemLayout, spacing: 20) {
            ForEach(appleSymbols.indices, id: \.self) { item in
                Image(systemName: appleSymbols[item])
                    .font(.system(size: 30))
                    .frame(width: 80, height: 80)
                    .background(bgColor)
                    .cornerRadius(8)
                    .onTapGesture {
                        logoImage = appleSymbols[item]
                    }
            }
        }
    }.padding()
}
复制代码

上述代码中,当我们创建了一个图标网格,然后遍历appleSymbols图片数组的数据,在点击每个图片时,我们把点击的图片赋值到logo区域中logoImage的变量,就实现了切换图标的效果。

我们来运行下看看效果:

image.png

同理,图标填充色我们就把背景颜色那块内容复制一份过来,然后当点击颜色的时候,赋值给logoColor变量就完成了,这里就不做演示了。

分段选项切换

最后是分段器的切换,我们可以根据selectedSegment变量作为切换状态值,当selectedSegment处于不同的值时,我们切换不同的视图。示例:

VStack(spacing: 60) {
    Spacer()
    ShowLogoView
    Spacer()
    VStack(spacing: -10) {
        SegmentView
        if selectedSegment == 0 {
            BGColorView
        } else if selectedSegment == 1 {
            SwitchIconView
        } else {
            LogoColorView
        }
    }
}
复制代码

完成后,我们尝试更改Logo背景颜色、Logo图标、Logo填充色来看看效果。

image.png

功能进阶

完成上述内容后,总觉得好像还差点东西,思来想去总算发现一个问题。

当前我们的颜色和Logo图标都是内置的,没有办法自定义。背景颜色和填充色还好,但是Logo图片不能自定义,就…….不够优雅

接下来,我们来实现自定义Logo图标的方法,最简单的就是本地上传图片

我们设想下通用的交互,当我们点击图标时,它弹出一个弹窗,让我们选择修改的来源。常用的图片来源有2种:相册、拍照,这里我们只完成相册的部分,毕竟应该很少人会直接拍照作为Logo图标。

image.png

打开Sheet弹窗

我们使用官方提供的Sheet弹窗来完成来源选择交互,示例:

// MARK: - 选择来源弹窗
private var chooseImageSheet: ActionSheet {
    let action = ActionSheet(title: Text("选择来源"), buttons: [.default(Text("相册"), action: {
        // 打开相册
    }), .cancel(Text("取消"), action: {
    })])
    return action
}
复制代码

上述代码中,我们构建了一个打开Sheet弹窗的视图chooseImageSheet,它的类型是ActionSheet,即触发Sheet弹窗

然后我们完善了Sheet弹窗里的相关内容,标题、操作(相册、取消)。

然后我们需要声明一个变量,判断是否打开Sheet弹窗,示例:

@State var showChooseImageSheet: Bool = false
复制代码

完成后,我们在LogoView中使用.actionSheet修饰符实现打开Sheet弹窗的交互。示例:

// 选择来源
.actionSheet(isPresented: $showChooseImageSheet, content: {chooseImageSheet })
复制代码

image.png

选择本地图片

我们已经成功调用Sheet弹窗了,可选项为相册取消,取消即自动收起Sheet弹窗,而当我们点击相册的时候,需要调用本地相册

接下来,我们来实现唤起本地相册的功能。

我们新建一个Swift文件,命名为ImagePickerView,然后实现以下方法:

import SwiftUI
public struct ImagePickerView: UIViewControllerRepresentable {
    private let sourceType: UIImagePickerController.SourceType
    private let onImagePicked: (UIImage) -> Void
    @Environment(\.presentationMode) private var presentationMode
    public init(sourceType: UIImagePickerController.SourceType, onImagePicked: @escaping (UIImage) -> Void) {
        self.sourceType = sourceType
        self.onImagePicked = onImagePicked
    }
    public func makeUIViewController(context: Context) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.sourceType = self.sourceType
        picker.delegate = context.coordinator
        return picker
    }
    public func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
    public func makeCoordinator() -> Coordinator {
        Coordinator(
            onDismiss: { self.presentationMode.wrappedValue.dismiss() },
            onImagePicked: self.onImagePicked
        )
    }
    final public class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        private let onDismiss: () -> Void
        private let onImagePicked: (UIImage) -> Void
        init(onDismiss: @escaping () -> Void, onImagePicked: @escaping (UIImage) -> Void) {
            self.onDismiss = onDismiss
            self.onImagePicked = onImagePicked
        }
        public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
            if let image = info[.originalImage] as? UIImage {
                self.onImagePicked(image)
            }
            self.onDismiss()
        }
        public func imagePickerControllerDidCancel(_: UIImagePickerController) {
            self.onDismiss()
        }
    }
}
复制代码

image.png

上述代码中,创建了一个公开的结构体ImagePickerView,遵循UIViewControllerRepresentable协议。

然后调用系统的UIImagePickerController选择图片方法,调用成功后关闭弹窗并且返回选中的图片

代码中大部分内容为实现选择图片的相关协议和返回UIImage类型图片的方法,在这里就不赘述了。

我们回到LogoView文件中,我们先声明必要的参数。示例:

@State var showImagePicker: Bool = false
@State var image: UIImage?
复制代码

上述代码中,showImagePicker是我们是否打开选择图片的弹窗,而image则用来接收我们相册中选择的图片。

我们使用.sheet调用打开本地相册的方法。示例:

// 本地图片选择弹窗
.sheet(isPresented: $showImagePicker) {
    ImagePickerView(sourceType: .photoLibrary) { image in
        self.image = image
    }
}
复制代码

我们使用showImagePicker绑定了打开本地图片弹窗的条件,我们把这个触发条件放在我们点击“相册”选项的操作。示例:

image.png

图标图片替换

上述代码中,我们已经通过调用本地相册的方法,获得了选中的图片,这时我们需要替换当前Logo的图标。

我们创建多一个视图来展示选中图片后回调的内容。示例:

// MARK: 本地图片
private var ShowImageView: some View {
    Image(uiImage: image!)
        .resizable()
        .frame(width: 68, height: 68, alignment: .center)
        .clipShape(Circle())
        .padding(20)
        .background(bgColor)
        .cornerRadius(8)
        .onTapGesture {
            self.showChooseImageSheet.toggle()
        }
}
复制代码

上述代码中,我们创建了一个新的视图ShowImageViewImage图片的内容换成了换取本地图库中选中回调的image

图标图片部分,使用clipShape修饰符切成圆形比较美观,背景颜色可调节,图标由于是图标就无需进行填充色调整了。

我们在LogoView视图中根据image的有无,展示不同的视图。效果如下:

image.png

项目预览

image.png

恭喜你,完成了本章的所有内容!

快来动手试试吧!


相关文章
|
25天前
|
存储 人工智能 安全
LangChain-23 Vector stores 向量化存储 并附带一个实际案例 通过Loader加载 Embedding后持久化 LangChain ChatOpenAI ChatGLM3对话
LangChain-23 Vector stores 向量化存储 并附带一个实际案例 通过Loader加载 Embedding后持久化 LangChain ChatOpenAI ChatGLM3对话
40 0
|
3月前
|
自然语言处理
预训练模型STAR问题之Doc2Bot数据集中结构信息的问题如何解决
预训练模型STAR问题之Doc2Bot数据集中结构信息的问题如何解决
|
6月前
|
搜索推荐 机器人 数据建模
使用 ChatGPT Store 里的工具给 ABAP 制作 Logo,效果一般
使用 ChatGPT Store 里的工具给 ABAP 制作 Logo,效果一般
SAP Fiori Elements 应用里图片字段(Image)的显示原理介绍试读版
SAP Fiori Elements 应用里图片字段(Image)的显示原理介绍试读版
SAP ABAP 一个有用的程序正确性辅助工具,Checkpoint group 的使用方法介绍试读版
SAP ABAP 一个有用的程序正确性辅助工具,Checkpoint group 的使用方法介绍试读版
|
缓存 iOS开发 C++
iOS 逆向编程(二十三)dsc_extractor 动态库提取器
iOS 逆向编程(二十三)dsc_extractor 动态库提取器
181 1
|
API 图形学
【Unity细节】RigidBody中Dynamic和Kinematic的区别
【Unity细节】RigidBody中Dynamic和Kinematic的区别
140 0
|
存储 安全 程序员
Libra教程之:move语言的特点和例子
Libra教程之:move语言的特点和例子
Libra教程之:move语言的特点和例子
|
Web App开发 移动开发 前端开发
巧用 display: contents 增强页面语义
巧用 display: contents 增强页面语义
385 1
SwiftUI—使用Image图像视图显示项目中的图片
SwiftUI—使用Image图像视图显示项目中的图片
638 0
SwiftUI—使用Image图像视图显示项目中的图片