Posts

Post not yet marked as solved
1 Replies
1.7k Views
Even though SwiftUI offers a lot of features for building a UI, I found my self missing something. I'm trying to display a list with calendar information, one item pr. day. I want to scroll this fluently and by the nature of a calendar, there is not a specific start or end to the list, so a standard ScrollView or List does not suit the task.I've therefore tried to put a custom control together. My approach is to have 2 items more that needed, initially positioned off screen (at the top and bottom). All items have the same height to keep the layout simple. I scroll by offsetting the items. Once the offset is greater than the height of an item, I shift the contents by 1 and reposition the items so I again have the first and last item placed off screen.This method works quite well, as long a the item views are simple. When the individual items get complex, I see a stuttering effect when I drag it up and down. I suspect that the delay causing the stutter comes from the view being regenerated.Is this approach good, and if so, what can I do to optimize it to reduce/remove the stuttering effect? If not, are there any alternate ideas for a solution?I included a rough version of the source code here to give an idea of the design:struct InfinityScroller<Item:View> : View { @State private var offset : CGFloat = 0 @State private var itemIndex : Int = 0 @State private var itemOffset : CGFloat private let itemHeight : CGFloat private let zero : CGFloat private let item : (Int,CGFloat,CGFloat) -> Item private let itemCount : Int = 7 private let offScreenCount : Int = 1 init(location:ObservedObject<InfinityLocation>,zero:CGFloat,_ height:CGFloat=121,@ViewBuilder item : @escaping (Int,CGFloat,CGFloat) -> Item) { self._location = location self.zero = zero self.itemHeight = height self.item = item self._itemOffset = State(initialValue: zero) } var body : some View { GeometryReader { g in ZStack(alignment:.top) { Color.clear.frame(width:0) Color.clear.frame(height:0) self.makeItems(g.size.width) } .contentShape(Rectangle()) .gesture(DragGesture(minimumDistance: 0) .onChanged { g in self.updatePosition(g.translation.height) } .onEnded { g in self.offset += g.translation.height self.updatePosition() } ) } } func updatePosition(_ scroll:CGFloat=0) { let off = offset + scroll + zero itemIndex = -Int((off/itemHeight).rounded(.down)) itemOffset = off.mod(itemHeight) } func makeItems(_ w:CGFloat) -> some View { return ForEach (-offScreenCount...itemCount+offScreenCount,id:\.self) { i in self.item(i+self.itemIndex,w,self.itemHeight) .position(x:0,y:CGFloat(i) * self.itemHeight + self.itemOffset) .offset(x:w/2,y:self.itemHeight/2) } } }
Posted
by jensrodi.
Last updated
.
Post marked as solved
3 Replies
5.5k Views
Hi,Using SwiftUI I'm trying to implement a long press gesture on items in a list, to allow user interaction with the individual items. The problem is that when I set "onLongPressGesture" anywhere in the list (on items, on the list itself), the list cannot be scrolled anymore. I can easily get a simple tap to work but a long press blocks scrolling.I've been Googling this for a couple of days now without any success. Hope someone can point me in the right direction.Yours,Jens
Posted
by jensrodi.
Last updated
.
Post marked as solved
1 Replies
857 Views
While retrieving metadata from media files, I've run into a memory issue I cannot figure out.I want to retrieve metadata for media files either stored in the local app storage or in the iTunes area. For this I use AVAsset. While looping through these files I can see the memory consumption rising constantly. And not just a little. It is significant and end up stalling the app when I enumerate my iTunes library on the phone.The problem seems to be accessing the metadata property on the AVAsset class. I've narrowed in down to one line of code: 'let meta = ***.metadata'. Having that line of code (without any references) makes the app consume memory. I've included an example of my code structure.func processFiles(_ files:Array) { var lastalbum : String = "" var i : Int = 0 for file in files { i += 1 view.setProgressPosition(CGFloat(i)/CGFloat(files.count)) lastalbum = updateFile(file.url,lastalbum, { (album,title,artist,composer) in view.setProgressNote(album,title,artist+" / "+composer) }) } } func updateFile(_ url:URL,_ lastalbum:String,iPod:Bool=false, _ progress:(String,String,String,String) -> Void) -> String { let *** = AVAsset(url:url) let meta = ***.metadata for item in meta { // Examine metadata } // Use metadata // Callback with status }It seems that memory allocated in the updateFile method, is kept, even when the function is ended. However, once the processFile function completes and the app returns to a normal state, all memory is released again.So in conclusion, this is not a real leak, but still a significant problem. Any good ideas as to what goes wrong? Is there any way I can force the memory management to run a cleanup?
Posted
by jensrodi.
Last updated
.
Post not yet marked as solved
0 Replies
300 Views
I have a view, that covers the entire screen except safe areas and handles DragGesture to perform navigation/scrolling. I find that when I do a rapid swipe up from the bottom edge to do a task switch on an iPhone 11 Pro, this is detected as a DragGesture on my view. This is as such not a problem, but I never get the onEnded method called and is left in an undefined state when my app reenters later on.I've put togther a very simple example that illustrates the problem:struct ContentView: View { @State var pos : CGFloat = 0 var body: some View { VStack { Color.clear.frame(height:0) Spacer() Text("\(pos)") Spacer() } .background(Color.gray) .contentShape(Rectangle()) .gesture(DragGesture() .onChanged { g in self.pos = g.translation.height } .onEnded { g in self.pos = 0 } ) } }Though the problem can orrur swiping up from any part of the bottom edge, it always triggers the problem if started at either the left of right corner of the screen. Please note that the setting and clearing the pos value is a simple way of illustrating the problem. The real app code for onEnded is not that simple.Is there any way to detect that the swipe has initiated a task switch so I can set my state accordingly (i.e. simulate a onEnded)? I there a way to avoid having the DragGesture triggering when the gesture realy is a system task switch?
Posted
by jensrodi.
Last updated
.
Post marked as solved
11 Replies
9.3k Views
I have an SwiftUI app with a ScrollView. When I change the contents, while the ScrollView is scrolled a bit down, the scroll position is kept. I would like the that changes to data resets this scroll position, i.e. scroll to the top. I've included a small example of this behavior.Scroll down a bit and press the "Toogle" text. Content changes but the position stays the same. How can I signal the ScrollView to 'reset' the scrolling?struct ContentView: View { static let data1 = [ "Test 1","Test 2","Test 3","Test 4","Test 5", "Test 6","Test 7","Test 8","Test 9","Test 10", "Test 11","Test 12","Test 13","Test 14","Test 15", "Test 16","Test 17","Test 18","Test 19","Test 20" ] static let data2 = [ "Test 101","Test 102","Test 103","Test 104","Test 105", "Test 106","Test 107","Test 108","Test 109","Test 110", "Test 111","Test 112","Test 113","Test 114","Test 115", "Test 116","Test 117","Test 118","Test 119","Test 120", "Test 121","Test 122","Test 123","Test 124","Test 125" ] @State var idx = 0 @State var data : [String] = data1 var body: some View { VStack { Text("Toggle") .onTapGesture { self.idx = 1-self.idx self.data = self.idx == 0 ? ContentView.data1 : ContentView.data2 } ScrollView { VStack { ForEach(self.data,id:\.self) { item in Text("\(self.idx) \(item)").padding(20) } } .frame(width:400) } } } }
Posted
by jensrodi.
Last updated
.
Post marked as solved
6 Replies
3k Views
Is it possible to have multiple independent context menus within a list row without these being displayed together as on menu?I put together a small example to illustrate the question. The desired behavior is that each of the two Text(...) views have their own contextmenu, which is only displayed when pressing that text.In the example, I can long press anywhere on the list row and will be presented with a context menu with 4 choices, the two menus appended to each other.struct ContentView: View { let data = [ "Test 1","Test 2","Test 3","Test 4","Test 5", "Test 6","Test 7","Test 8","Test 9","Test 10", "Test 11","Test 12","Test 13","Test 14","Test 15", "Test 16","Test 17","Test 18","Test 19","Test 20" ] var body: some View { List { ForEach(data,id:\.self) { item in HStack { Text(item+" A") .contextMenu { Button(action: {}) { Text("Item one"); Image(systemName: "globe") } Button(action: {}) { Text("Item two"); Image(systemName: "location.circle") } } Spacer() Text(item+" B") .contextMenu { Button(action: {}) { Text("Item one"); Image(systemName: "globe") } Button(action: {}) { Text("Item two"); Image(systemName: "location.circle") } } } } } } }
Posted
by jensrodi.
Last updated
.