Say you have a UICollectionView with a normal custom UICollectionViewLayout.
So that is >>> NOT <<< a flow layout - it's a normal custom layout.
Custom layouts are trivial, in the prepare call you simply walk down the data and lay out each rectangle. So say it's a vertical scrolling collection...
override func prepare() {
cache = []
var y: CGFloat = 0
let k = collectionView?.numberOfItems(inSection: 0) ?? 0
// or indeed, just get that direct from your data
for i in 0 ..< k {
// say you have three cell types ...
let h = ... depending on the cell type, say 100, 200 or 300
let f = CGRect(
origin: CGPoint(x: 0, y: y ),
size: CGSize(width: screen width, height: h)
)
y += thatHeight
y += your gap between cells
cache.append( .. that one)
}
}
In the example the cell height is just fixed for each of the say three cell types - all no problem.
Handling dynamic cell heights if you are using a flow layout is well-explored and indeed relatively simple. (Example, also see many explanations on the www.)
However, what if you want dynamic cell heights with a (NON-flow) completely normal everyday UICollectionViewLayout?
Where's the estimatedItemSize ?
As far as I can tell, there is NO estimatedItemSize concept in UICollectionViewLayout?
So what the heck do you do?
You could naively just - in the code above - simply calculate the final heights of each cell one way or the other (so for example calculating the height of any text blocks, etc). But that seems perfectly inefficient: nothing at all of the collection view, can be drawn, until the entire 100s of cell sizes are calculated. You would not at all be using any of iOS's dynamic heights power and nothing would be just-in-time.
I guess, you could program an entire just-in-time system from scratch. (So, something like .. make the table size actually only 1, calculate manually that height, send it along to the collection view; calculate item 2 height, send that along, and so on.) But that's pretty lame.
Is there any way to achieve dynamic height cells with a custom UICollectionViewLayout - NOT a flow layout?
(Again, of course obviously you could just do it manually, so in the code above calculate all at once all 1000 heights, and you're done, but that would be pretty lame.)
Like I say above the first puzzle is, where the hell is the "estimated size" concept in (normal, non-flow) UICollectionViewLayout?
Just a warning: custom layouts are FAR from trivial, they may deserve a research paper on their own ;)
You can implement size estimation and dynamic sizing in your own layouts. Actually, estimated sizes are nothing special; rather, dynamic sizes are. Because custom layouts give you a total control of everything, however, this involves many steps. You will need to implement three methods in your layout subclass and one method in your cells.
First, you need to implement preferredLayoutAttributesFitting(_:) in your cells (or, more generally, reusable views subclass). Here you can use whatever calculations you want. Chances are that you will use auto layout with your cells: if so, you will need to add all cell's subviews to its contentView, constrain them to the edges and then call systemLayoutSizeFitting(_:withHorizontalFittingPriority:verticalFittingPriority:) within this "preferred attributes" method. For example, if you want your cell to resize vertically, while being constrained horizontally, you would write:
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
// Ensures that cell expands horizontally while adjusting itself vertically.
let preferredSize = systemLayoutSizeFitting(layoutAttributes.size, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
layoutAttributes.size = preferredSize
return layoutAttributes
}
After the cell is asked for its preferred attributes, the shouldInvalidateLayout(forPreferredLayoutAttributes:withOriginalAttributes:) on the layout object will be called. What's important, you can't just simply type return true, since the system will reask the cell indefinitely. This is actually very clever, since many cells may react to each other's changes, so it's the layout who ultimately decides if it's done satisfying the cells' wishes. Usually, for resizing, you would write something like this:
override func shouldInvalidateLayout(forPreferredLayoutAttributes preferredAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -> Bool {
if preferredAttributes.size.height.rounded() != originalAttributes.size.height.rounded() {
return true
}
return false
}
Just after that, invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:) will be called. You usually would want to customize the context class to store the information specific to your layout. One important, rather unintuitive, caveat though is that you should not call context.invalidateItems(at:) because this will cause the layout to invalidate only those items among the provided index paths that are actually visible. Just skip this method, so the layout will requery the visible rectangle.
However! You need to thoroughly think if you need to set contentOffsetAdjustment and contentSizeAdjustment: if something resizes, your collection view as a whole probably will shrink or expand. If you do not account for those, you will have jump-reloads when scrolling.
Lastly, invalidateLayout(with:) will be called. This is the step that's intended for you to actually adjust your sections/rows heights, move something that's been affected by the resizing cell etc. If you override, you will need to call super.
PS: This is really a hard topic, I just scratched the surface. You can look here how complicated it gets (but this repo is also a very rich learning tool).
I wrote a little program that copies the frame and position of a ROI from an image to an other image of the same size.
What I want to do now is to connect the two ROIs in way that when I move one ROI the other one is moving accordingly.
On Dave's mitchell DM scripting website, I found that he used the function ConnectObject. but he does not explain how it works.
I read the DM3's documentation and I couldn't find any information about that function.
There are two concepts here which would work. You can use one of two methods:
1) Use "ConnectObject" to attach some functionality to when a ROI is moved, i.e. when you move ROI 1 it "triggers" code which you can use to update other rois.
2) Use "ImageDisplayListeners" to attach functionality to when any ROI on a specific imageDisplay is moved,
i.e. when a ROI an image A is moved it triggers code which you can use to update other rois.
You will find example code in this answer.
For simple things there is another option:
Adding the identical ROI to more than one image-display:
In this case, the ROIs are "linked" automatically, because they really are only a single object in memory (but displayed on two displays.) Changing one will change the other.
However, this linkage is "lost" if you save/load the images, because when you load the image, all ROIs (in memory) are newly created. Here is some simple example code:
image img1, img2
GetTwoLabeledImagesWithPrompt("Select two images of same size.", "Select", "Source", img1, "Destination", img2 )
imageDisplay disp1 = img1.ImageGetImageDisplay( 0 )
imageDisplay disp2 = img2.ImageGetImageDisplay( 0 )
number nR = disp1.ImageDisplayCountROIs()
for ( number i = 0; i<nR; i++ )
{
ROI theROI = disp1.ImageDisplayGetROI(i)
disp2.ImageDisplayAddROI(theROI)
}
While elm-make succeeds, I get the following error in the browser:
Cannot read property 'kids' of undefined
I assume it's because I have a circular dependency of signals:
model -> clicks -> model
Here is the relevant code:
model : Signal Model
model =
Signal.foldp update initialModel clicks
clicks : Signal Action
clicks =
let
clickPosition = Mouse.position
|> Signal.sampleOn Mouse.clicks
tuplesSignal = Signal.map3 (,,) clickPosition Window.dimensions model
in
...
It feels like model is implemented as a common practice in Elm, so I should challenge the clicks -> model dependency.
Here is some context:
I'm building a sliding puzzle game using canvas:
When user clicks a tile that can move, it should move. Otherwise, the click should be ignored.
clicks will produce the following actions: Left, Right, Up, Down
For example, if user clicks on tiles 12, 11, 8, 15 (in this order), clicks should be: Down -> Right -> Up
The problem is, that in order to calculate which tile was clicked, I need to know the board dimensions (e.g. 4 rows and 4 columns in the picture above). But, board dimensions are stored in the model (imagine user interface that allows users to change board dimensions).
How do I get out of this circular dependency?
In this case I think you should go more low-level in what you call an input, and accept click positions as inputs. Then you can compose an update function in your Signal.foldp out of two others:
The first turns the clicks and model into a Maybe Direction (assuming the Left, Right, Up, Down are constructors of type Direction)
The second function that takes a Direction value and the model to calculate the new model. You can use Maybe.map and Maybe.withDefault to handle the Maybe part of the result of the first function.
Separating these two parts, even though you produce and consume Direction immediately, makes your system more self-documenting and shows the conceptual split between the raw input and the restricted "actual" inputs.
P.S. There are conceivable extensions to the Elm language that would allow you to more easily write this input preprocessing in signal-land. But such extensions would make the signal part of the language so much more powerful that it's unclear if it would make "the right way" to structure programs harder to discover.
I am writing a roguelike in Elm, where there is a discrete 50x50 grid (see share-elm.com snippet). A roguelike is a video game, where objects (like enemies, items, walls, etc) are represented by ASCII characters. Therefore I should be able to have hundreds of different ASCII characters, aligned in a rectangular grid. Every character should be strictly within its grid cell.
To create this grid, I put every character in a square container (1/50 size of the actual game container). This means I can have 2500 containers in the game maximum. Elm creates <div> elements for containers, even if I convert these containers to Form and put them inside a collage. This makes my Firefox 39.0 very slow in performance.
How do I create a rectangular grid with nicely aligned ASCII characters (and possibly some other graphical elements) within its grid cells, so that no matter how many elements I have at the same time, the collage still stays quick and responsive? And what is the general idiomatic approach every time I'm writing a program with lots of containers and other elements inside a collage? Or maybe there is a completely different approach to creating snappy rectangular grids in Elm?
One possibility (if you don't mind writing some HTML instead of using collage/container) would be to use the Html.Lazy module. You could, for example, wrap the rendering of each "row" of the display in a lazy and it would only re-render the rows that changed (which should only be 1-2 per timestep/movement).
What you're looking for here is Graphics.Collage.text. When you turn an Element into a Form Elm will take the general approach that can place any Element like a Form, but it doesn't actually draw it on the canvas. (Yay, implementation details). If you instead go straight from Text to Form, it's statically known that it's text, so the faster method of drawing text on a canvas can be used. This is a simple change:
view : (Int, Int) -> Element
view (w,h) =
let
s = min w h -- collageSize
forms = List.map (\(x,y) -> move (s,s) (x,y) playerForm)
<| cartesian 0 (screenSize-1) 0 (screenSize-1)
playerForm = "#"
|> Text.fromString
|> Text.height ((toFloat s) / screenSize)
|> C.text
-- |> E.centered
-- |> E.container (s//screenSize) (s//screenSize) E.middle
-- |> C.toForm
in
E.color Color.lightGray
<| E.container w h E.middle
<| E.color Color.white
<| C.collage s s forms
Instead of the three lines in comments, it's just the C.text. You can see the responsiveness in the updated share-elm snippet.
Note that you can no longer select the text! But otherwise it should be much better.
I've been learning f# in the previous days, writing a small project which, at last, works (with the help of SO, of course).
I'm trying to learn to be as idiomatic as possible, which basically means that I try to not mutate my data structures. This is costing me a lot of effort :-)
In my search for idiomatic functional programming, I have been trying to use as much as possible lists, tuples and record, rather than objects. But then "praticality beats purity" and so I'm rewriting my small project using objects this time.
I thought that you could give me some advice, surely my idea of "good functional programming design" is not yet very well defined.
For instance I have to modify the nodes of a tree, modifying at the same time the states at two different levels (L and L+1). I've been able to do that without mutating data, but I needed a lot of "inner" and "helper" functions, with accumulators and so on. The nice feeling of being able to clearly express the algorithm was lost for me, due to the need to modify my data structure in an involved way. This is extremely easy in imperative languages, for instance: just dereference the pointers to the relevant nodes, modify their state and iterate over.
Surely I've not designed properly my structure, and for this reason I'm now trying the OOP approach.
I've looked at SICP, at How to design programs and have found a thesis by C. Okasaki ("Purely functional data structures") but the examples on SICP and HTDP are similar to what I did, or maybe I'm not able to understand them fully. The thesis on the other hand is a bit too hard for me at the moment :-)
What do you think about this "tension" which I am experiencing? Am I interpreting the "never mutate data" too strictly? Could you suggest me some resource?
Thanks in advance,
Francesco
When it comes to 'tree update', I think you can always do it pretty elegantly
using catamorphisms (folds over trees). I have a long blog series about this,
and most of the example code below comes from part 4 of the series.
When first learning, I find it best to focus on a particular small, concrete
problem statement. Based on your description, I invented the following problem:
You have a binary tree, where each node contains a "name" and an "amount" (can
think of it like bank accounts or some such). And I want to write a function
which can tell someone to "steal" a certain amount from each of his direct
children. Here's a picture to describe what I mean:
alt text http://oljksa.bay.livefilestore.com/y1pNWjpCPP6MbI3rMfutskkTveCWVEns5xXaOf-NZlIz2Hs_CowykUmwtlVV7bPXRwh4WHJMT-5hSuGVZEhmAIPuw/FunWithTrees.png
On the left I have an original tree. The middle example shows the result I want if node
'D' is asked to steal '10' from each of his children. And the right example
shows what the desired result is if instead I asked 'F' to steal '30' in the original example.
Note that the tree structure I use will be immutable, and the red colors in the
diagram designate "new tree nodes" relative to the original tree. That is black
nodes are shared with the original tree structure (Object.ReferenceEquals to one
another).
Now, assuming a typical tree structure like
type Tree<'T> = //'
| Node of 'T * Tree<'T> * Tree<'T> //'
| Leaf
we'd represent the original tree as
let origTree = Node(("D",1000),
Node(("B",1000),
Node(("A",1000),Leaf,Leaf),
Node(("C",1000),Leaf,Leaf)),
Node(("F",1000),
Node(("E",1000),Leaf,Leaf),
Leaf))
and the "Steal" function is really easy to write, assuming you have the usual "fold"
boilerplate:
// have 'stealerName' take 'amount' from each of its children and
// add it to its own value
let Steal stealerName amount tree =
let Subtract amount = function
| Node((name,value),l,r) -> amount, Node((name,value-amount),l,r)
| Leaf -> 0, Leaf
tree |> XFoldTree
(fun (name,value) left right ->
if name = stealerName then
let leftAmt, newLeft = Subtract amount left
let rightAmt, newRight = Subtract amount right
XNode((name,value+leftAmt+rightAmt),newLeft,newRight)
else
XNode((name,value), left, right))
XLeaf
// examples
let dSteals10 = Steal "D" 10 origTree
let fSteals30 = Steal "F" 30 origTree
That's it, you're done, you've written an algorithm that "updates" levels L and
L+1 of an immutable tree just by authoring the core logic. Rather than explain
it all here, you should go read my blog series (at least the start: parts one two three four).
Here's all the code (that drew the picture above):
// Tree boilerplate
// See http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!248.entry
type Tree<'T> =
| Node of 'T * Tree<'T> * Tree<'T>
| Leaf
let (===) x y = obj.ReferenceEquals(x,y)
let XFoldTree nodeF leafV tree =
let rec Loop t cont =
match t with
| Node(x,left,right) -> Loop left (fun lacc ->
Loop right (fun racc ->
cont (nodeF x lacc racc t)))
| Leaf -> cont (leafV t)
Loop tree (fun x -> x)
let XNode (x,l,r) (Node(xo,lo,ro) as orig) =
if xo = x && lo === l && ro === r then
orig
else
Node(x,l,r)
let XLeaf (Leaf as orig) =
orig
let FoldTree nodeF leafV tree =
XFoldTree (fun x l r _ -> nodeF x l r) (fun _ -> leafV) tree
// /////////////////////////////////////////
// stuff specific to this problem
let origTree = Node(("D",1000),
Node(("B",1000),
Node(("A",1000),Leaf,Leaf),
Node(("C",1000),Leaf,Leaf)),
Node(("F",1000),
Node(("E",1000),Leaf,Leaf),
Leaf))
// have 'stealerName' take 'amount' from each of its children and
// add it to its own value
let Steal stealerName amount tree =
let Subtract amount = function
| Node((name,value),l,r) -> amount, Node((name,value-amount),l,r)
| Leaf -> 0, Leaf
tree |> XFoldTree
(fun (name,value) left right ->
if name = stealerName then
let leftAmt, newLeft = Subtract amount left
let rightAmt, newRight = Subtract amount right
XNode((name,value+leftAmt+rightAmt),newLeft,newRight)
else
XNode((name,value), left, right))
XLeaf
let dSteals10 = Steal "D" 10 origTree
let fSteals30 = Steal "F" 30 origTree
// /////////////////////////////////////////
// once again,
// see http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!248.entry
// DiffTree: Tree<'T> * Tree<'T> -> Tree<'T * bool>
// return second tree with extra bool
// the bool signifies whether the Node "ReferenceEquals" the first tree
let rec DiffTree(tree,tree2) =
XFoldTree (fun x l r t t2 ->
let (Node(x2,l2,r2)) = t2
Node((x2,t===t2), l l2, r r2)) (fun _ _ -> Leaf) tree tree2
open System.Windows
open System.Windows.Controls
open System.Windows.Input
open System.Windows.Media
open System.Windows.Shapes
// Handy functions to make multiple transforms be a more fluent interface
let IdentT() = new TransformGroup()
let AddT t (tg : TransformGroup) = tg.Children.Add(t); tg
let ScaleT x y (tg : TransformGroup) = tg.Children.Add(new ScaleTransform(x, y)); tg
let TranslateT x y (tg : TransformGroup) = tg.Children.Add(new TranslateTransform(x, y)); tg
// Draw: Canvas -> Tree<'T * bool> -> unit
let Draw (canvas : Canvas) tree =
// assumes canvas is normalized to 1.0 x 1.0
FoldTree (fun ((name,value),b) l r trans ->
// current node in top half, centered left-to-right
let tb = new TextBox(Width=100.0, Height=100.0, FontSize=30.0, Text=sprintf "%s:%d" name value,
// the tree is a "diff tree" where the bool represents
// "ReferenceEquals" differences, so color diffs Red
Foreground=(if b then Brushes.Black else Brushes.Red),
HorizontalContentAlignment=HorizontalAlignment.Center,
VerticalContentAlignment=VerticalAlignment.Center)
tb.RenderTransform <- IdentT() |> ScaleT 0.005 0.005 |> TranslateT 0.25 0.0 |> AddT trans
canvas.Children.Add(tb) |> ignore
// left child in bottom-left quadrant
l (IdentT() |> ScaleT 0.5 0.5 |> TranslateT 0.0 0.5 |> AddT trans)
// right child in bottom-right quadrant
r (IdentT() |> ScaleT 0.5 0.5 |> TranslateT 0.5 0.5 |> AddT trans)
) (fun _ -> ()) tree (IdentT())
let TreeToCanvas tree =
let canvas = new Canvas(Width=1.0, Height=1.0, Background = Brushes.Blue,
LayoutTransform=new ScaleTransform(400.0, 400.0))
Draw canvas tree
canvas
let TitledControl title control =
let grid = new Grid()
grid.ColumnDefinitions.Add(new ColumnDefinition())
grid.RowDefinitions.Add(new RowDefinition())
grid.RowDefinitions.Add(new RowDefinition())
let text = new TextBlock(Text = title, HorizontalAlignment = HorizontalAlignment.Center)
Grid.SetRow(text, 0)
Grid.SetColumn(text, 0)
grid.Children.Add(text) |> ignore
Grid.SetRow(control, 1)
Grid.SetColumn(control, 0)
grid.Children.Add(control) |> ignore
grid
let HorizontalGrid (controls:_[]) =
let grid = new Grid()
grid.RowDefinitions.Add(new RowDefinition())
for i in 0..controls.Length-1 do
let c = controls.[i]
grid.ColumnDefinitions.Add(new ColumnDefinition())
Grid.SetRow(c, 0)
Grid.SetColumn(c, i)
grid.Children.Add(c) |> ignore
grid
type MyWPFWindow(content, title) as this =
inherit Window()
do
this.Content <- content
this.Title <- title
this.SizeToContent <- SizeToContent.WidthAndHeight
[<System.STAThread()>]
do
let app = new Application()
let controls = [|
TitledControl "Original" (TreeToCanvas(DiffTree(origTree,origTree)))
TitledControl "D steals 10" (TreeToCanvas(DiffTree(origTree,dSteals10)))
TitledControl "F steals 30" (TreeToCanvas(DiffTree(origTree,fSteals30))) |]
app.Run(new MyWPFWindow(HorizontalGrid controls, "Fun with trees")) |> ignore
I guess if you start your sentence with " I have to modify the nodes of a tree, modifying at the same time the states at two different levels" then you're not really tackling your problem in a functional way. It's like writing a paper in a foreign language by first writing it in your mother tongue, then trying to translate. Doesn't really work. I know it hurts, but in my opinion it's better to immerse yourself completely. Don't worry about comparing the approaches just yet.
One way I've found to learn "the functional way" is to look at (and implement yourself!) some functional pearls. They're basically well document uber-functional elegant progams to solve a variety of problems. Start with the older ones, and don't be afraid to stop reading and try another one if you don't get it. Just come back to it later with renewed enthousiasm and more experience. It helps :)
What do you think about this "tension"
which I am experiencing? Am I
interpreting the "never mutate data"
too strictly? Could you suggest me
some resource?
In my opinion, if you're learning functional programming for the first time, its best to start out with zero mutable state. Otherwise, you'll only end up falling back on mutable state as your first resort, and all of your F# code will be C# with a little different syntax.
Regarding data structures, some are easier to express in a functional style than others. Could you provide a description of how you're trying to modify your tree?
For now, I would recommend F# Wikibook's page on data structures to see how data structures are written in a functional style.
I've looked at SICP, at How to design
programs and have found a thesis by C.
Okasaki ("Purely functional data
structures")
I personally found Okasaki's book more readable than the thesis online.
I have to modify nodes of a tree.
No you don't. That's your problem right there.
This is costing me a lot of effort
This is typical. It's not easy to learn to program with immutable data structures. And to most beginners, it seems unnatural at first. It's doubly difficult because HTDP and SICP don't give you good models to follow (see footnote).
I thought that you could give me some advice, surely my idea of "good functional programming design" is not yet very well defined.
We can, but you have to tell us what the problem is. Then many people on this forum can tell you if it is the sort of problem whose solution can be expressed in a clear way without resorting to mutation. Most tree problems can. But with the information you've given us, there's no way for us to tell.
Am I interpreting the "never mutate data" too strictly?
Not strictly enough, I'd say.
Please post a question indicating what problem you're trying to solve.
Footnote: both HTDP and SICP are done in Scheme, which lacks pattern matching. In this setting it is much harder to understand tree-manipulation code than it is using the pattern matching provided by F#. As far as I'm concerned, pattern matching is an essential feature for writing clear code in a purely functional style. For resources, you might consider Graham Hutton's new book on Programming in Haskell.
Take a look at the Zipper data structure.
For instance I have to modify the nodes of a tree, modifying at the same time the states at two different levels (L and L+1)
Why? In a functional language, you'd create a new tree instead. It can reuse the subtrees which don't need to be modified, and just plug them into a newly created root. "Don't mutate data" doesn't mean "try to mutate data without anyone noticing, and by adding so many helper methods that no one realize that this is what you're doing".
It means "don't mutate your data. Create new copies instead, which are initialized with the new, correct, values".