DetailView/货币详情页 的实现

news2024/11/20 19:45:00

1. 创建货币详情数据模型类 CoinDetailModel.swift

import Foundation

// JSON Data
/*
 URL:
 https://api.coingecko.com/api/v3/coins/bitcoin?localization=false&tickers=false&market_data=false&community_data=false&developer_data=false&sparkline=false
 
 Response:
 {
   "id": "bitcoin",
   "symbol": "btc",
   "name": "Bitcoin",
   "asset_platform_id": null,
   "platforms": {
     "": ""
   },
   "detail_platforms": {
     "": {
       "decimal_place": null,
       "contract_address": ""
     }
   },
   "block_time_in_minutes": 10,
   "hashing_algorithm": "SHA-256",
   "categories": [
     "Cryptocurrency",
     "Layer 1 (L1)"
   ],
   "public_notice": null,
   "additional_notices": [],
   "description": {
     "en": "Bitcoin is the first successful internet money based on peer-to-peer technology; whereby no central bank or authority is involved in the transaction and production of the Bitcoin currency. It was created by an anonymous individual/group under the name, Satoshi Nakamoto. The source code is available publicly as an open source project, anybody can look at it and be part of the developmental process.\r\n\r\nBitcoin is changing the way we see money as we speak. The idea was to produce a means of exchange, independent of any central authority, that could be transferred electronically in a secure, verifiable and immutable way. It is a decentralized peer-to-peer internet currency making mobile payment easy, very low transaction fees, protects your identity, and it works anywhere all the time with no central authority and banks.\r\n\r\nBitcoin is designed to have only 21 million BTC ever created, thus making it a deflationary currency. Bitcoin uses the <a href=\"https://www.coingecko.com/en?hashing_algorithm=SHA-256\">SHA-256</a> hashing algorithm with an average transaction confirmation time of 10 minutes. Miners today are mining Bitcoin using ASIC chip dedicated to only mining Bitcoin, and the hash rate has shot up to peta hashes.\r\n\r\nBeing the first successful online cryptography currency, Bitcoin has inspired other alternative currencies such as <a href=\"https://www.coingecko.com/en/coins/litecoin\">Litecoin</a>, <a href=\"https://www.coingecko.com/en/coins/peercoin\">Peercoin</a>, <a href=\"https://www.coingecko.com/en/coins/primecoin\">Primecoin</a>, and so on.\r\n\r\nThe cryptocurrency then took off with the innovation of the turing-complete smart contract by <a href=\"https://www.coingecko.com/en/coins/ethereum\">Ethereum</a> which led to the development of other amazing projects such as <a href=\"https://www.coingecko.com/en/coins/eos\">EOS</a>, <a href=\"https://www.coingecko.com/en/coins/tron\">Tron</a>, and even crypto-collectibles such as <a href=\"https://www.coingecko.com/buzz/ethereum-still-king-dapps-cryptokitties-need-1-billion-on-eos\">CryptoKitties</a>."
   },
   "links": {
     "homepage": [
       "http://www.bitcoin.org",
       "",
       ""
     ],
     "blockchain_site": [
       "https://blockchair.com/bitcoin/",
       "https://btc.com/",
       "https://btc.tokenview.io/",
       "https://www.oklink.com/btc",
       "https://3xpl.com/bitcoin",
       "",
       "",
       "",
       "",
       ""
     ],
     "official_forum_url": [
       "https://bitcointalk.org/",
       "",
       ""
     ],
     "chat_url": [
       "",
       "",
       ""
     ],
     "announcement_url": [
       "",
       ""
     ],
     "twitter_screen_name": "bitcoin",
     "facebook_username": "bitcoins",
     "bitcointalk_thread_identifier": null,
     "telegram_channel_identifier": "",
     "subreddit_url": "https://www.reddit.com/r/Bitcoin/",
     "repos_url": {
       "github": [
         "https://github.com/bitcoin/bitcoin",
         "https://github.com/bitcoin/bips"
       ],
       "bitbucket": []
     }
   },
   "image": {
     "thumb": "https://assets.coingecko.com/coins/images/1/thumb/bitcoin.png?1547033579",
     "small": "https://assets.coingecko.com/coins/images/1/small/bitcoin.png?1547033579",
     "large": "https://assets.coingecko.com/coins/images/1/large/bitcoin.png?1547033579"
   },
   "country_origin": "",
   "genesis_date": "2009-01-03",
   "sentiment_votes_up_percentage": 73.21,
   "sentiment_votes_down_percentage": 26.79,
   "watchlist_portfolio_users": 1326950,
   "market_cap_rank": 1,
   "coingecko_rank": 1,
   "coingecko_score": 83.151,
   "developer_score": 99.241,
   "community_score": 83.341,
   "liquidity_score": 100.011,
   "public_interest_score": 0.073,
   "public_interest_stats": {
     "alexa_rank": 9440,
     "bing_matches": null
   },
   "status_updates": [],
   "last_updated": "2023-08-11T08:43:13.856Z"
 }
 */


/// 交易货币详情模型
struct CoinDetailModel: Codable {
    let id, symbol, name: String?
    let blockTimeInMinutes: Int?
    let hashingAlgorithm: String?
    let description: Description?
    let links: Links?
    
    enum CodingKeys: String, CodingKey {
        case id, symbol, name
        case blockTimeInMinutes = "block_time_in_minutes"
        case hashingAlgorithm = "hashing_algorithm"
        case description, links
    }
    
    /// 去掉 HTML 链接的描述
    var readableDescription: String? {
        return description?.en?.removingHTMLOccurances
    }
}

struct Description: Codable {
    let en: String?
}

struct Links: Codable {
    let homepage: [String]?
    let subredditURL: String?
    
    enum CodingKeys: String, CodingKey {
        case homepage
        case subredditURL = "subreddit_url"
    }
}

2. 创建货币详情数据服务类 CoinDetailDataService.swift

import Foundation
import Combine

/// 交易货币详情服务
class CoinDetailDataService{
    // 交易货币详情模型数组 Published: 可以拥有订阅者
    @Published var coinDetails: CoinDetailModel? = nil
    // 随时取消操作
    var coinDetailSubscription: AnyCancellable?
    // 传入的货币模型
    let coin: CoinModel
    
    init(coin: CoinModel) {
        self.coin = coin
        getCoinDetails()
    }
    
    /// 获取交易硬币详情
    func getCoinDetails(){
        guard let url = URL(string: "https://api.coingecko.com/api/v3/coins/\(coin.id)?localization=false&tickers=false&market_data=false&community_data=false&developer_data=false&sparkline=false")
        else { return }
        
        coinDetailSubscription = NetworkingManager.downLoad(url: url)
            .decode(type: CoinDetailModel.self, decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: NetworkingManager.handleCompletion,
                  receiveValue: { [weak self] returnCoinDetails in
                // 解除强引用 (注意)
                self?.coinDetails = returnCoinDetails
                // 取消订阅者
                self?.coinDetailSubscription?.cancel()
            })
    }
}

3. 创建货币详情 ViewModel 类,DetailViewModel.swift

import Foundation
import Combine

/// 交易货币详情 ViewModel
class DetailViewModel: ObservableObject {
    /// 概述统计模型数组
    @Published var overviewStatistics: [StatisticModel] = []
    /// 附加统计数据数组
    @Published var additionalStatistics: [StatisticModel] = []
    /// 货币描述
    @Published var description: String? = nil
    /// 货币官网网站
    @Published var websiteURL: String? = nil
    /// 货币社区网站
    @Published var redditURL: String? = nil
    
    /// 交易货币模型
    @Published var coin: CoinModel
    /// 交易货币详情请求服务
    private let coinDetailService: CoinDetailDataService
    /// 随时取消订阅
    private var cancellables = Set<AnyCancellable>()
    
    init(coin: CoinModel) {
        self.coin = coin
        self.coinDetailService = CoinDetailDataService(coin: coin)
        self.addSubscribers()
    }
    
    /// 添加订阅者
    private func addSubscribers(){
        // 订阅货币详情数据
        coinDetailService.$coinDetails
            .combineLatest($coin)
             // 数据的转换
            .map(mapDataToStatistics)
            .sink {[weak self] returnedArrays in
                self?.overviewStatistics = returnedArrays.overview
                self?.additionalStatistics = returnedArrays.additional
            }
            .store(in: &cancellables)
        
        // 订阅货币详情数据
        coinDetailService.$coinDetails
            .sink {[weak self] returnedCoinDetails in
                self?.description = returnedCoinDetails?.readableDescription
                self?.websiteURL = returnedCoinDetails?.links?.homepage?.first
                self?.redditURL = returnedCoinDetails?.links?.subredditURL
            }
            .store(in: &cancellables)
    }
    
    /// 数据转换为统计信息数据
    private func mapDataToStatistics(coinDetailModel: CoinDetailModel?, coinModel: CoinModel) -> (overview: [StatisticModel], additional: [StatisticModel]){
        // 概述信息
        // 当前货币概述信息
        let overviewArray = createOvervierArray(coinModel: coinModel)
        // 附加信息
        // 当前货币附加信息
        let additionalArray = createAdditionalArray(coinModel: coinModel, coinDetailModel: coinDetailModel)
        // 返回数组
        return (overviewArray, additionalArray)
    }
    
    /// 创建概述信息数组
    private func createOvervierArray(coinModel: CoinModel) -> [StatisticModel]{
        // 当前交易货币价格
        let price = coinModel.currentPrice.asCurrencyWith6Decimals()
        // 当前交易货币价格 24 小时的变化百分比
        let pricePercentChange = coinModel.priceChangePercentage24H
        // 当前交易货币价格 统计信息
        let priceStat = StatisticModel(title: "Current Price", value: price, percentageChange: pricePercentChange)
        
        // 市值 价格
        let marketCap = "$" + (coinModel.marketCap?.formattedWithAbbreviations() ?? "")
        // 市值 24 小时变化百分比
        let marketCapPercentChange = coinModel.marketCapChangePercentage24H
        // 市值 统计信息
        let marketCapStat = StatisticModel(title: "Market Capitalization", value: marketCap, percentageChange: marketCapPercentChange)
        
        // 当前交易货币的排名
        let rank = "\(coinModel.rank)"
        // 当前货币排名 统计信息
        let rankStat = StatisticModel(title: "Rank", value: rank)
        
        // 交易总量
        let volume = coinModel.totalVolume?.formattedWithAbbreviations() ?? ""
        // 交易 统计信息
        let volumeStat = StatisticModel(title: "Volume", value: volume)
        
        // 当前货币概述信息
        return [priceStat, marketCapStat, rankStat, volumeStat]
    }
    
    /// 创建附加信息数组
    private func createAdditionalArray(coinModel: CoinModel, coinDetailModel: CoinDetailModel?) -> [StatisticModel]{
        // 24 小时内最高点
        let high = coinModel.high24H?.asCurrencyWith6Decimals() ?? "n/a"
        // 最高点 统计信息
        let highStat = StatisticModel(title: "24h High", value: high)
        
        // 24 小时内最低点
        let  low = coinModel.low24H?.asCurrencyWith6Decimals() ?? "n/a"
        // 最低点 统计信息
        let lowStat = StatisticModel(title: "24h Low", value: low)
        
        // 24 小时内价格变化
        let priceChange = coinModel.priceChange24H?.asCurrencyWith6Decimals() ?? "n/a"
        // 当前交易货币 24 小时的价格变化百分比
        let pricePercentChange2 = coinModel.priceChangePercentage24H
        // 24 小时内价格变化 统计信息
        let priceChangeStat = StatisticModel(title: "24h Price Change", value: priceChange, percentageChange: pricePercentChange2)
        
        // 24 小时内市值变化值
        let marketCapChange = "$" + (coinModel.marketCapChange24H?.formattedWithAbbreviations() ?? "")
        // 市值 24 小时变化百分比
        let marketCapPercentChange2 = coinModel.marketCapChangePercentage24H
        // 24 小时内市值变换 统计信息
        let marketCapChangeStat = StatisticModel(title: "24h Market Cap Change", value: marketCapChange, percentageChange: marketCapPercentChange2)
        
        // 区块时间 (分钟为单位)
        let blockTime = coinDetailModel?.blockTimeInMinutes ?? 0
        let blockTimeString = blockTime == 0 ? "n/a" : "\(blockTime)"
        // 统计信息
        let blockTimeStat = StatisticModel(title: "Block Time", value: blockTimeString)
        
        // 哈希/散列 算法
        let hashing = coinDetailModel?.hashingAlgorithm ?? "n/a"
        let hashingStat = StatisticModel(title: "Hashing Algorithm", value: hashing)
        
        // 当前货币附加信息
        return [highStat, lowStat, priceChangeStat, marketCapChangeStat, blockTimeStat, hashingStat]
    }
}

4. 货币详情 View/视图 层

  4.1 创建折线视图 ChartView.swift

import SwiftUI

/// 折线视图
struct ChartView: View {
    // 7 天价格中的交易货币数据
    private let data: [Double]
    // Y 最大值
    private let maxY: Double
    // Y 最小值
    private let minY: Double
    // 线条颜色
    private let lineColor: Color
    // 开始日期
    private let startingDate: Date
    // 结束日期
    private let endingDate: Date
    // 绘制折线进度的百分比
    @State private var percentage: CGFloat = 0
    
    init(coin: CoinModel) {
        data = coin.sparklineIn7D?.price ?? []
        maxY = data.max() ?? 0
        minY = data.min() ?? 0
        
        // 最后一个价格减去第一个价格,为当前的价格变化量
        let priceChange = (data.last ?? 0) - (data.first ?? 0)
        // 线条颜色
        lineColor = priceChange > 0 ? Color.theme.green : Color.theme.red
        
        // 转换开始结束时间格式
        endingDate = Date(coinGeckoString: coin.lastUpdated ?? "")
        // 没有返回开始时间,根据他是结束日期的前七天,所以定义为结束时间之前间隔为 -7 天
        startingDate = endingDate.addingTimeInterval(-7 * 24 * 60 * 60)
    }
    
    // 计算 X 点的位置:
    // 300 : Viw 的宽
    // 100 : 数据个数
    // 3   : 得到的增量 300 / 100
    // 1 * 3 = 3  : x 位置 的计算
    // 2 * 3 = 6
    // 3 * 3 = 9
    // 100 * 3 = 300
    
    // 计算 Y 点的位置
    // 60,000 -> 最大值
    // 50,000 -> 最小值
    // 60,000 - 50,000 = 10,000 -> yAxis / Y轴
    // 52,000 - data point
    // 52,000 - 50,000 = 2,000 / 10,000 = 20%
    
    var body: some View {
        VStack {
            // 折线视图
            chartView
                .frame(height: 200)
                .background(chartBackground)
                .overlay(chartYAxis.padding(.horizontal, 4), alignment: .leading)
            // X 轴日期文字
            chartDateLabels
                .padding(.horizontal, 4)
        }
        .font(.caption)
        .foregroundColor(Color.theme.secondaryText)
        .onAppear {
            // 线程
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
                // 线程具有动画效果
                withAnimation(.linear(duration: 2.0)) {
                    percentage = 1.0
                }
            }
        }
    }
}

struct ChartView_Previews: PreviewProvider {
    static var previews: some View {
        ChartView(coin: dev.coin)
    }
}

extension ChartView{
    /// 折线视图
    private var chartView: some View{
        // GeometryReader: 根据试图大小自动布局页面
        GeometryReader{ geometry in
            Path { path in
                for index in data.indices{
                    // x 点的位置  宽度 / 总数 * 增量
                    let xPosition =  geometry.size.width / CGFloat(data.count) * CGFloat(index + 1)
                    
                    // Y 轴
                    let yAxis = maxY - minY
                    
                    // y 点的位置
                    let yPosition = (1 - CGFloat((data[index] - minY) / yAxis)) * geometry.size.height
                    
                    if index == 0 {
                        // 移至起始点左上角(0,0)
                        path.move(to: CGPoint(x: xPosition, y: yPosition))
                    }
                    // 添加一条线
                    path.addLine(to: CGPoint(x: xPosition, y: yPosition))
                }
            }
            // 修剪绘制线条的进度
            .trim(from: 0, to: percentage)
            .stroke(lineColor, style: StrokeStyle(lineWidth: 2, lineCap: .round, lineJoin: .round))
            .shadow(color: lineColor, radius: 10, x: 0.0, y: 10)
            .shadow(color: lineColor.opacity(0.5), radius: 10, x: 0.0, y: 20)
            .shadow(color: lineColor.opacity(0.2), radius: 10, x: 0.0, y: 30)
            .shadow(color: lineColor.opacity(0.1), radius: 10, x: 0.0, y: 40)
        }
    }
    
    /// 背景
    private var chartBackground: some View{
        VStack{
            Divider()
            Spacer()
            Divider()
            Spacer()
            Divider()
        }
    }
    
    /// Y 轴坐标点文字
    private var chartYAxis: some View{
        VStack{
            Text(maxY.formattedWithAbbreviations())
            Spacer()
            Text(((maxY + minY) * 0.5).formattedWithAbbreviations())
            Spacer()
            Text(minY.formattedWithAbbreviations())
        }
    }
    
    /// X 轴日期文字
    private var chartDateLabels: some View{
        HStack {
            Text(startingDate.asShortDateString())
            Spacer()
            Text(endingDate.asShortDateString())
        }
    }
}

  4.2 创建货币详情视图,DetailView.swift

import SwiftUI

/// 加载交易货币详情页
struct DetailLoadingView: View{
    /// 交易货币模型
    @Binding var coin: CoinModel?
    
    var body: some View {
        ZStack {
            if let coin = coin{
                DetailView(coin: coin)
            }
        }
    }
}

/// 交易货币详情页
struct DetailView: View {
    @StateObject private var viewModel: DetailViewModel
    /// 是否展开概述内容
    @State private var showFullDescription: Bool = false
    
    // 网格样式
    private let colums: [GridItem] = [
        // flexible: 自动调整大小
        GridItem(.flexible()),
        GridItem(.flexible())
    ]
    // 网格间隔
    private let spacing: CGFloat = 30
    
    /// 交易货币模型
    init(coin: CoinModel) {
        _viewModel = StateObject(wrappedValue: DetailViewModel(coin: coin))
    }
    
    var body: some View {
        ScrollView {
            VStack {
                // 交易货币折线图
                ChartView(coin: viewModel.coin)
                // 间隔
                    .padding(.vertical)
                VStack(spacing: 20) {
                    // 概述信息标题
                    overviewTitle
                    Divider()
                    // 概述信息内容
                    descriptionSection
                    // 概述信息网格 View
                    overviewGrid
                    // 附加信息标题
                    additionalTitle
                    Divider()
                    // 附加信息网格 View
                    additionalGrid
                    //  网站地址
                    websiteSection
                }
                .padding()
            }
        }
        .background(Color.theme.background.ignoresSafeArea())
        .navigationTitle(viewModel.coin.name)
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                navigationBarTrailing
            }
        }
    }
}

extension DetailView{
    /// 导航栏右边 Item,建议使用 Toolbar
    private var navigationBarTrailing: some View{
        HStack {
            Text(viewModel.coin.symbol.uppercased())
                .font(.headline)
                .foregroundColor(Color.theme.secondaryText)
            CoinImageView(coin: viewModel.coin)
                .frame(width: 25, height: 25)
        }
    }
    
    /// 概述信息标题
    private var overviewTitle: some View{
        Text("Overview")
            .font(.title)
            .bold()
            .foregroundColor(Color.theme.accent)
            .frame(maxWidth: .infinity, alignment: .leading)
    }
    
    ///  附加信息标题
    private var additionalTitle: some View{
        Text("Additional Details")
            .font(.title)
            .bold()
            .foregroundColor(Color.theme.accent)
            .frame(maxWidth: .infinity, alignment: .leading)
    }
    
    /// 概述信息内容
    private var descriptionSection: some View{
        ZStack {
            if let description = viewModel.description,
               !description.isEmpty {
                
                VStack(alignment: .leading) {
                    // 描述文本
                    Text(description)
                        .lineLimit(showFullDescription ? nil : 3)
                        .font(.callout)
                        .foregroundColor(Color.theme.secondaryText)
                    // 更多按钮
                    Button {
                        withAnimation(.easeInOut) {
                            showFullDescription.toggle()
                        }
                    } label: {
                        Text(showFullDescription ? "Less" : "Read more...")
                            .font(.caption)
                            .fontWeight(.bold)
                            .padding(.vertical, 4)
                    }
                    .accentColor(.blue)
                }
                .frame(maxWidth: .infinity, alignment: .leading)
            }
        }
    }
    
    /// 概述信息网格 View
    private var overviewGrid: some View{
        LazyVGrid(
            columns: colums,
            alignment: .leading,
            spacing: spacing,
            pinnedViews: []) {
                ForEach(viewModel.overviewStatistics) { stat in
                    StatisticView(stat:stat)
                }
            }
    }
    
    /// 附加信息网格 View
    private var additionalGrid: some View{
        LazyVGrid(
            columns: colums,
            alignment: .leading,
            spacing: spacing,
            pinnedViews: []) {
                ForEach(viewModel.additionalStatistics) { stat in
                    StatisticView(stat: stat)
                }
            }
    }
    
    /// 网站地址
    private var websiteSection: some View{
        VStack(alignment: .leading, spacing: 12){
            // 官方网站
            if let websiteString = viewModel.websiteURL,
               let url = URL(string: websiteString){
                Link("Website", destination: url)
            }
            
            Spacer()
            // 论坛网站
            if let redditString = viewModel.redditURL,
               let url = URL(string: redditString){
                Link("Reddit", destination: url)
            }
        }
        .accentColor(.blue)
        .frame(maxWidth: .infinity, alignment: .leading)
        .font(.headline)
    }
}

struct DetailView_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            DetailView(coin: dev.coin)
        }
    }
}

5. 效果图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1081253.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

滚珠螺母在工业机器人中的应用优势

工业机器人是广泛用于工业领域的多关节机械手或多自由度的机器装置&#xff0c;具有一定的自动性&#xff0c;可依靠自身的动力能源和控制能力实现各种工业加工制造功能。滚珠螺母作为工业机器人中的重要传动配件&#xff0c;在工业机器人的应用中有哪些优势呢&#xff1f; 1、…

华为云云耀云服务器L实例评测 | 实例评测使用之硬件参数评测:华为云云耀云服务器下的监控 glances

华为云云耀云服务器L实例评测 &#xff5c; 实例评测使用之硬件参数评测&#xff1a;华为云云耀云服务器下的监控 glances 介绍华为云云耀云服务器 华为云云耀云服务器 &#xff08;目前已经全新升级为 华为云云耀云服务器L实例&#xff09; 华为云云耀云服务器是什么华为云云耀…

【操作系统】信号量机制及PV操作问题总结

【操作系统】信号量机制及PV操作问题总结 文章目录 【操作系统】信号量机制及PV操作问题总结题型分类解题的基本思路1、前置知识 &#xff08;信号量机制&#xff09;&#xff08;1&#xff09;整型信号量&#xff08;2&#xff09;记录型信号量&#xff08;3&#xff09;信号量…

PCB板子上一坨黢黑的可不简单,你知道吗?

有些电路板上我们会看到这么一坨黑色的东西&#xff0c;其实这是一种封装工艺&#xff0c;我们称之为软封装&#xff0c;也叫邦定封装。 它是芯片生产工艺中一种打线的方式&#xff0c;一般用于封装前将芯片内部的电路用金线与封装管脚连接&#xff0c;是裸芯片贴装技术之一&am…

36 WEB漏洞-逻辑越权之验证码与Token及接口

目录 验证码安全token安全接口安全问题未授权访问涉及案例验证码识别插件及工具操作演示-实例验证码绕过本地及远程验证-本地及实例Token客户端回显绕过登录爆破演示-本地Callback自定义返回调用安全-漏洞测试-实例补&#xff1a;上述在实战中如何做到漏洞发现-bp功能点 文章分…

C++ PCL点云曲率分割颜色标识

程序示例精选 C PCL点云曲率分割颜色标识 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《C PCL点云曲率分割颜色标识》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学…

信息系统项目管理师第四版学习笔记——配置与变更管理

配置管理 管理基础 配置管理是为了系统地控制配置变更&#xff0c;在信息系统项目的整个生命周期中维持配置的完整性和可跟踪性&#xff0c;而标识信息系统建设在不同时间点上配置的学科。 配置项的版本号规则与配置项的状态定义相关。例如&#xff1a;①处于“草稿”状态的…

strncpy,strncat,strncmp字符串函数详解(长度受限制)

目录 一&#xff0c;strncpy函数 1&#xff0c;strncpy函数简介 2&#xff0c;strncpy函数示例 3&#xff0c;注意事项 二&#xff0c;strncat函数 1&#xff0c;strncat函数简介 2&#xff0c;strncat函数示例 3&#xff0c;注意事项 三&#xff0c;strncmp函数 1&…

通讯录的完善(文件操作)

目录 前言&#xff1a; 保存联系人 读取联系人&#xff1a; 总结&#xff1a; 前言&#xff1a; 对于我们之前利用动态内存实现的通讯录来说&#xff0c;存在一个严重的问题。 就是当我们的程序运行结束&#xff0c;此时我们在通讯录所添加的全部联系人信息会全部丢失。 …

网页设计学习记录-常用圆角按钮css

.button {border: 0px;padding: 5px;border-radius: 100px;background-color: #9eb80c;width: 150px;height: 50px;color: white; } .button:hover {cursor: pointer; } 效果图

【AN-Animate教程——了解AN用途】

【AN-Animate教程——了解AN用途】 Animate是啥Animate能做什么2D动画制作帧动画制作矢量图形绘制和编辑角色建模与骨骼绑定动画特效和过渡效果动画导出与发布 除了动画还能做什么&#xff1f; 这一段时间没更新&#xff0c;主要是工作生活陷入了一个瓶颈。本想着阅读一些人工智…

安卓三防平板在行业应用中有哪些优势

在工业维修和检测中&#xff0c;安卓三防平板的应用也十分广泛。它可以搭载各种专业软件和工具&#xff0c;帮助工人们进行设备故障排查和维护&#xff0c;降低了维修成本和停机时间。 一、产品卖点&#xff1a; 1. 防水性能&#xff1a;该手持平板采用了防水设计&#xff0c;…

go mod 使用三方包、go get命令

一、环境变量设置 go env -w GO111MODULEon go env -w GOPROXYhttps://goproxy.cn,https://goproxy.io,direct 二、goland开启 go mod 三、go mod 使用 在go.mod文件中声明三方包地址&版本号即可&#xff0c;如下&#xff1a; 开发工具goland会自动解析go.mod文件&#x…

Vscode中使用Romote远程开发调试Ros2环境

首先&#xff0c;成功安装ros2环境&#xff0c;参考官方文档中的教程&#xff0c;能用运行出来此处的代码 Writing a simple publisher and subscriber (Python) — ROS 2 Documentation: Iron documentation 下载vscode&#xff0c;进行远程开发&#xff0c;具体参考&#xf…

微软 AR 眼镜新专利:包含热拔插电池

近日&#xff0c;微软在增强现实&#xff08;AR&#xff09;领域进行深入的研究&#xff0c;并申请了一项有关于“热插拔电池”的专利。该专利于2023年10月5日发布&#xff0c;描述了一款采用模块化设计的AR眼镜&#xff0c;其热插拔电池放置在镜腿部分&#xff0c;可以直接替代…

视频监控管理系统EasyCVR+智能分析网关如何为银行安保系统保驾护航

银行是国家重点安防保护单位&#xff0c;现金流通性高、人员复杂&#xff0c;对高精度的安全监控尤为需要。特别在当今社会&#xff0c;高智商犯罪分子层出不穷&#xff0c;恶性案件也屡屡发生。传统的监控模式已经无法满足银行的安保要求&#xff0c;而现在市面上融合了智能化…

交直流回馈式电子负载的优势和特点

交直流回馈式电子负载是用于测试和模拟电源、电池和其他电子设备的负载装置&#xff0c;它能够承受大范围的负载电流和电压&#xff0c;可以满足不同类型电子设备的测试需求。该负载装置具有精确的电流和电压测量功能&#xff0c;可以提供准确的测试结果&#xff0c;帮助用户评…

淘宝商品评论数据接口,淘宝商品评论API接口

淘宝商品评论数据接口可以通过淘宝开放平台API获取。 通过构建合理的请求URL&#xff0c;可以向淘宝服务器发起HTTP请求&#xff0c;获取商品评论数据。接口返回的数据一般为JSON格式&#xff0c;包含了商品的各种评价信息。获取到商品评论数据后&#xff0c;可以对其进行处理…

Spring WebClient 基于响应式编程模型的HTTP客户端

一、简介 WebClient是一个非阻塞的、可扩展的、基于Reactive Streams规范的HTTP客户端。它提供了一种简洁的方式来进行HTTP请求&#xff0c;并且可以很好地与其他Spring组件集成。WebClient支持同步和异步操作&#xff0c;使得它非常适合用于构建响应式应用程序。 WebClient允…

全网都在用的英语配音软件~

现在有很多智能配音软件都可以输入文字内容生成语音&#xff0c;让我们给视频配音的过程更加简单方便。但目前很多比较常见的文字转语音软件都只能配音中文&#xff0c;而无法配音英语。想要智能生成英语的配音的小伙伴看过来&#xff0c;给大家推荐一款超实用的英语配音软件&a…