

以下のコードで、★1、 ★2ではtitleList配列の値が取れているのに、★3で値が取れない理由と、対応方法がわかりませんでした。

[code]

    import SwiftUI
    import UIKit
    
    struct NewsView: View {
        
        var newsMap = NewsApiMap()
        @State var titleList = []
        @State var url = []
        @State var image = []
        
        // ニュースを取得して編集
        func getData(data : News) {
            self.titleList = []
            url = []
            image = []
            for  n:Int in (0 ... (data.articles.count - 1 )){
                
                self.titleList.append(data.articles[n].title)
                self.url.append(data.articles[n].url)
                self.image.append(data.articles[n].urlToImage)
            }
            // ★1：titleList[0]に値が入っている
            print(titleList[0])
        }
        
    
        var body: some View{
            
            VStack  {
                VStack{
                    Button(action: {newsMap.getNews(action: self.getData) }) {
                        Text("画面表示時にとってきたい")
                            .fontWeight(.bold).font(.title)
                            .foregroundColor(Color.black)
                    }
                    List{
                        // ★2：　titleList.countは取得できる
                        Text("\(titleList.count)").foregroundColor(.blue)
                        // ★3：　titleList[0]は取得できない
                    Text("\(titleList[0])").foregroundColor(.blue)
                        
                    }
                }
            }
        }
    }
    
    struct NewsView_Previews: PreviewProvider {
        static var previews: some View {
            NewsView()
        }
    }
    
[/code]

情報が足りておらず申し訳ありません。NewsとArticleの定義は以下です。

[code]

    struct News: Codable {
        let status: String
        let totalResults: Int
        let articles: [Article]
    }
    
    
    struct Article: Codable {
        let title: String
        let url: String
        let urlToImage: String
    }
    
[/code]

また、Text("()")では配列のAny型は使えないと教えていただいたので、  
Articleを以下のｎｅｗｓＬｉｓｔに格納し直す処理をeditDataメゾッドに入れました。

[code]

    @State var newsList:[(title:String, url: String, urlToImage: String )] = []
    
[/code]

[code]

        // ニュースを取得して編集
        func getData(data : News) {
            newsList = []
            
            for var i:Int in (0 ... (data.articles.count - 1)){
                self.newsList.append((data.articles[i].title,  data.articles[i].url,  data.articles[i].urlToImage))
                i += 1
            }
        }
    
[/code]


CC BY-SA 4.0
2021-01-10T05:21:21.893
73184
2021-01-11T08:48:11.113
2021-01-11T07:06:08.293
43477
43477
question
swift
値を保持できる範囲について


「値が取れない」と言うのは何を表しているのでしょうか？ここでは『「★3」のコメントの付いた行がコンパイルエラーになってしまう』と捉えて回答を書いて見ます。もしエラーになるのであれば、ご質問の際にはエラーメッセージを含めるようにしてください。

また「対応方法」については、ご質問中のコードに含まれる`NewsApiMap`や`News`などの定義内容がわからないと確実なことが書けません、以下では一部必要な内容を推定していますが、これも質問の際には関連する(すでに含めているコード中で使われている)ものについては、可能な限りその定義を含めるようにしてください。

* * *

まず、あなたのコードの中で最も問題になる部分はここです。

[code]

        @State var titleList = []
        @State var url = []
        @State var image = []
    
[/code]

この3つのプロパティの宣言では型が明示されていないため、3つのプロパティの型は`[Any]`(`Array<Any>`に同じ)と型推論により決定されます。各要素の型は`Any`で、Swiftでは`Any`型に対して出来る操作はきわめて限られるので、
**こんな宣言をしていては、これからもどんどん変なエラーに取り憑かれることになります** 。

自分のコードの中では、 **`Any`型は可能な限り避けるようにする** べきでしょう。

追記された`News`と`Article`の定義によれば、`titleList`には、`String`型の要素しか入らないのですから、宣言でそのことを明示すべきです。

[code]

        @State var titleList: [String] = []
        @State var url: [String] = []
        @State var image: [String] = []
    
[/code]

これだけで「★3」の部分がエラーになることはないはず(こちらの仮定通りならば、ですが)なのですが、いかがでしょうか？

* * *

SwiftUIの`Text`に文字列補間リテラルを使用する場合、`\(...)`の中で使用可能な式の型は限られています。`String`型はOKですが、`Any`型は不可です。これは、`print(...)`の中ではどんなデータ型でもOKだったのとは全く異なります。

* * *

ただし、これはSwift, SwiftUIに限らずプログラミング一般に言えることですが、個々の要素をばらして`titleList`, `url`,
`image`のような複数の別々の配列として管理するのは、うまいやり方とは言えません。

以下のように`[Article]`型の配列として宣言した方が良いでしょう。

[code]

    struct NewsView: View {
        var newsMap = NewsApiMap()
        @State var articles: [Article] = []
        
        // ニュースを取得して編集
        func getData(data : News) {
            self.articles = data.articles
            // ★1：titleList[0]に値が入っている
            print(articles[0].title)
        }
    
        var body: some View{
            VStack  {
                VStack{
                    Button(action: {newsMap.getNews(action: self.getData) }) {
                        Text("画面表示時にとってきたい")
                            .fontWeight(.bold).font(.title)
                            .foregroundColor(.black)
                    }
                    List {
                        // ★2：　titleList.countは取得できる
                        Text("\(articles.count)").foregroundColor(.blue)
                        // ★3：　titleList[0]は取得できない
                        Text("\(articles[0].title)").foregroundColor(.blue)
                    }
                }
            }
        }
    }
    
[/code]

ちなみに、`articles[0]`のような参照は、`articles`が0件の場合にはクラッシュします。今後どのように発展させるのかわかりませんのでそのままにしましたが、気を付けた方が良いでしょう。

* * *

(追記部分のコードについて)  
直接の質問内容とは関係ありませんが、追記部分のコードについて明らかにおかしい点がありますので、そのことだけ指摘して置きます。

[code]

            for var i:Int in (0 ... (data.articles.count - 1)){
                //...
                i += 1
            }
    
[/code]

配列の要素全部に対して、このようなループを記述するのは明らかに不合理でおかしな書き方です。

  * `i`の値はfor-inの制御によって自動的に更新される。`i += 1`の行は意味を持たない
  * `(0 ... (data.articles.count - 1))`の部分はSwiftでは`0 ..< data.articles.count`のように半開範囲を使う方が普通
  * この構文では`i`は確実に`Int`型であると型推論可能、自明な型は書かない方が普通

結果、その部分のfor-inは次のように書くべき(or書く方が普通)です。

[code]

            for i in 0 ..< data.articles.count {
                self.newsList.append((data.articles[i].title,  data.articles[i].url,  data.articles[i].urlToImage))
            }
    
[/code]

(`i in 0 ..< data.articles.count`よりも`i in
data.articles.indices`の方が良いとか、`map`を使えばfor文自体が不要とか、`Article`の配列にしとけば変換自体要らない、とか言い出したらキリがありませんが。)

絶対にこう書かなければいけないと言うわけではありませんが、無駄な行はコード中に残しておくべきではありませんし、できるだけSwift的に普通の書き方をする癖をつけておいた方が、普通に書かれたSwiftのコードを読むのが楽になる上に、他のSwiftプログラマーがあなたのコードを読むときにも読みやすくなります。


CC BY-SA 4.0
2021-01-10T06:48:18.277
73187
2021-01-11T08:48:11.113
2021-01-11T08:48:11.113
13972
13972
73184
answer