-
-
Save hussam/0127e676a789fc704d8e to your computer and use it in GitHub Desktop.
EasyLayout makes writing auto layout code in Xamarin.iOS F# easier. (Unified API)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module EasyLayout | |
open System | |
open Microsoft.FSharp.Quotations | |
open Microsoft.FSharp.Quotations.Patterns | |
open Microsoft.FSharp.Quotations.DerivedPatterns | |
open Foundation | |
open UIKit | |
open CoreGraphics | |
module private Utilities = | |
let rec eval e = | |
match e with | |
| FieldGet (Some o, f) -> f.GetValue (eval o) | |
| FieldGet (None, f) -> f.GetValue (null) | |
| PropertyGet (None, p, i) -> p.GetValue (null, i |> Seq.map eval |> Seq.toArray) | |
| PropertyGet (Some o, p, i) -> p.GetValue (eval o, i |> Seq.map eval |> Seq.toArray) | |
| Value (x, _) -> x | |
| NewObject(nfloat, [Value(x, _)]) -> x | |
| _ -> raise (Exception (sprintf "Don't know how to eval %A" e)) | |
let toAttr m = | |
match m with | |
| "X" | "Left" -> NSLayoutAttribute.Left | |
| "Y" | "Top" -> NSLayoutAttribute.Top | |
| "Width" -> NSLayoutAttribute.Width | |
| "Height" -> NSLayoutAttribute.Height | |
| "Bottom" -> NSLayoutAttribute.Bottom | |
| "Right" -> NSLayoutAttribute.Right | |
| "CenterX" | "CGRect.get_CenterX" -> NSLayoutAttribute.CenterX | |
| "CenterY" | "CGRect.get_CenterY" -> NSLayoutAttribute.CenterY | |
| "Baseline" | "RectangleF.get_Baseline" -> NSLayoutAttribute.Baseline | |
| _ -> NSLayoutAttribute.NoAttribute | |
let isConstrainableProperty m = toAttr m <> NSLayoutAttribute.NoAttribute | |
let (|GetFrameProp|) e = | |
match e with | |
| Let (_, PropertyGet (Some o, fn, _), PropertyGet (_, pn, _)) | |
when fn.Name = "Frame" && isConstrainableProperty pn.Name -> | |
Some (eval o :?> NSObject, toAttr pn.Name) | |
| Call (_, pn, [PropertyGet (Some o, fn, _)]) | |
when fn.Name = "Frame" && isConstrainableProperty pn.Name -> | |
Some (eval o :?> NSObject, toAttr pn.Name) | |
| _ -> None | |
let compileLeftSide side = | |
match side with | |
| GetFrameProp (Some x) -> x | |
| _ -> raise (Exception (sprintf "Left hand side of constraint is expected to be a UIView.Frame property. It was: %A" side)) | |
let (|Mul|) side = | |
match side with | |
| GetFrameProp (Some (x, p)) -> Some (x, p, 1.0f) | |
| Call (_, m, [l; GetFrameProp (Some (x, p))]) when m.Name = "op_Multiply" -> Some (x, p, Convert.ToSingle (eval l)) | |
| Call (_, m, [GetFrameProp (Some (x, p)); l]) when m.Name = "op_Multiply" -> Some (x, p, Convert.ToSingle (eval l)) | |
| _ -> None | |
let compileRightSide side = | |
match side with | |
| Mul (Some x) -> (Some x, 0.0f) | |
| Call (_, mem, [Mul (Some x); c]) when mem.Name = "op_Addition" -> (Some x, Convert.ToSingle (eval c)) | |
| Call (_, mem, [Mul (Some x); c]) when mem.Name = "op_Subtraction" -> (Some x, -Convert.ToSingle (eval c)) | |
| Value (x, _) -> (None, Convert.ToSingle (x)) | |
| NewObject(nfloat, [Value (x, _)]) -> (None, Convert.ToSingle (x)) | |
| FieldGet _ -> (None, Convert.ToSingle (eval side)) | |
| _ -> raise (Exception (sprintf "Unrecognized right hand side: %A." side)) | |
let compileConstraint left right rel = | |
let (firstObj, firstAttr) = compileLeftSide left | |
let (maybeObj, add) = compileRightSide right | |
match maybeObj with | |
| None -> NSLayoutConstraint.Create (firstObj, firstAttr, rel, null, NSLayoutAttribute.NoAttribute, nfloat 0.0f, nfloat add) | |
| Some (secObj, secAttr, mul) -> NSLayoutConstraint.Create (firstObj, firstAttr, rel, secObj, secAttr, nfloat mul, nfloat add) | |
let toRel m = | |
match m with | |
| "op_Equality" -> Some NSLayoutRelation.Equal | |
| "op_LessThanOrEqual" -> Some NSLayoutRelation.LessThanOrEqual | |
| "op_GreaterThanOrEqual" -> Some NSLayoutRelation.GreaterThanOrEqual | |
| _ -> None | |
let rec compileConstraints expr = | |
match expr with | |
| NewArray (_, es) -> es |> Seq.collect compileConstraints |> Seq.toList | |
| IfThenElse (i, t, e) -> compileConstraints i @ compileConstraints t @ compileConstraints e | |
| Call (_, m, [l; r]) when (toRel m.Name).IsSome -> [compileConstraint l r (toRel m.Name).Value] | |
| Value _ -> [] | |
| _ -> raise (Exception (sprintf "Unable to recognize constraints in expression: %A" expr)) | |
type CGRect with | |
member this.CenterX = this.X + this.Width / (nfloat 2.0f) | |
member this.CenterY = this.Y + this.Height / (nfloat 2.0f) | |
member this.Baseline = nfloat 0.0f | |
type UIView with | |
/// <summary> | |
/// <para>Constrains the layout of subviews according to equations and | |
/// inequalities specified in <paramref name="constraints"/>. Issue | |
/// multiple constraints per call using the && operator.</para> | |
/// <code><@ button.Frame.Left >= text.Frame.Right + 22 && | |
/// button.Frame.Width = View.Frame.Width * 0.42f @></code> | |
/// </summary> | |
/// <param name="constraints">Constraint equations and inequalities.</param> | |
member this.ConstrainLayout (constraints) = | |
let cs = Utilities.compileConstraints constraints |> Seq.toArray | |
this.AddConstraints (cs) | |
for x in cs do | |
(x.FirstItem :?> UIView).TranslatesAutoresizingMaskIntoConstraints <- false | |
cs |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment