Skip to content

Positioning and Alignment

In this example, we will show you how to use the CreativeEditor SDK’s CreativeEngine to modify scenes layout through the block API.

Layout of Blocks

The CreativeEngine supports three different modes for positioning blocks. These can be set for each block and both coordinates independently:

  • 'Absolute': the position value is interpreted in the scene’s current design unit.
  • 'Percent': the position value is interpreted as percentage of the block’s parent’s size, where 1.0 means 100%.
  • 'Auto' : the position is automatically determined.

Likewise there are also three different modes for controlling a block’s size. Again both dimensions can be set independently:

  • 'Absolute': the size value is interpreted in the scene’s current design unit.
  • 'Percent': the size value is interpreted as percentage of the block’s parent’s size, where 1.0 means 100%.
  • 'Auto' : the block’s size is automatically determined by the size of the block’s content.

Positioning

public func getPositionX(_ id: DesignBlockID) throws -> Float

Query a block’s x position.

  • id:: The block to query.
  • Returns: The value of the x position.
public func getPositionY(_ id: DesignBlockID) throws -> Float

Query a block’s y position.

  • id:: The block to query.
  • Returns: The value of the y position.
public func getPositionXMode(_ id: DesignBlockID) throws -> PositionMode

Query a block’s mode for its x position.

  • id:: The block to query.
  • Returns: The current mode for the x position: absolute, percent or undefined.
public func getPositionYMode(_ id: DesignBlockID) throws -> PositionMode

Query a block’s mode for its y position.

  • id:: The block to query.
  • Returns: The current mode for the y position: absolute, percent or undefined.
public func setPositionX(_ id: DesignBlockID, value: Float) throws

Update a block’s x position. The position refers to the block’s local space, relative to its parent with the origin at the top left. Required scope: “layer/move”

  • id: The block to update.
  • value: The value of the x position.
public func setPositionY(_ id: DesignBlockID, value: Float) throws

Update a block’s y position. The position refers to the block’s local space, relative to its parent with the origin at the top left. Required scope: “layer/move”

  • id: The block to update.
  • value: The value of the y position.
public func setPositionXMode(_ id: DesignBlockID, mode: PositionMode) throws

Set a block’s mode for its x position. Required scope: “layer/move”

  • id: The block to update.
  • mode: The x position mode: absolute, percent or undefined.
public func setPositionYMode(_ id: DesignBlockID, mode: PositionMode) throws

Set a block’s mode for its y position. Required scope: “layer/move”

  • id: The block to update.
  • mode: The y position mode: absolute, percent or undefined.

Layers

public func setAlwaysOnTop(_ id: DesignBlockID, enabled: Bool) throws

Set a block to be always-on-top. If true, this blocks’s global sorting order is automatically adjusted to be higher than all other siblings without this property. If more than one block is set to be always-on-top, the child order decides which is on top.

  • id: The block to update.
  • enabled: The new state.
public func isAlwaysOnTop(_ id: DesignBlockID) throws -> Bool

If a block is set to be always-on-top.

  • id: The block to query.
public func setAlwaysOnBottom(_ id: DesignBlockID, enabled: Bool) throws

Set a block to be always-on-bottom. If true, this blocks’s global sorting order is automatically adjusted to be lower than all other siblings without this property. If more than one block is set to be always-on-bottom, the child order decides which is on the bottom.

  • id: The block to update.
  • enabled: The new state.
public func isAlwaysOnBottom(_ id: DesignBlockID) throws -> Bool

If a block is set to be always-on-bottom.

  • id: The block to query.
public func bringToFront(_ id: DesignBlockID) throws

Updates the sorting order of this block and all of its manually created siblings so that the given block has the highest sorting order. If the block is parented to a track, it is first moved up in the hierarchy.

  • id: The block to update.
public func sendToBack(_ id: DesignBlockID) throws

Updates the sorting order of this block and all of its manually created siblings so that the given block has the lowest sorting order. If the block is parented to a track, it is first moved up in the hierarchy.

  • id: The block to update.
public func bringForward(_ id: DesignBlockID) throws

Updates the sorting order of this block and all of its superjacent siblings so that the given block has a higher sorting order than the next superjacent sibling. If the block is parented to a track, it is first moved up in the hierarchy. Empty tracks and empty groups are passed by.

  • id: The block to update.
public func sendBackward(_ id: DesignBlockID) throws

Updates the sorting order of this block and all of its manually created and subjacent siblings so that the given block will have a lower sorting order than the next subjacent sibling. If the block is parented to a track, it is first moved up in the hierarchy. Empty tracks and empty groups are passed by.

  • id: The block to update.

Size

public func getWidth(_ id: DesignBlockID) throws -> Float

Query a block’s width.

  • id:: The block to query.
  • Returns: The value of the block’s width.
public func getWidthMode(_ id: DesignBlockID) throws -> SizeMode

Query a block’s mode for its width.

  • id:: The block to query.
  • Returns: The current mode for the width: absolute, percent or auto.
public func getHeight(_ id: DesignBlockID) throws -> Float

Query a block’s height.

  • id:: The block to query.
  • Returns: The value of the block’s height.
public func getHeightMode(_ id: DesignBlockID) throws -> SizeMode

Query a block’s mode for its height.

  • id:: The block to query.
  • Returns: The current mode for the height: absolute, percent or auto.
public func setWidth(_ id: DesignBlockID, value: Float, maintainCrop: Bool = false) throws

Update a block’s width and optionally maintain the crop. If the crop is maintained, the crop values will be automatically adjusted. The content fill mode Cover is only kept if the features/transformEditsRetainCoverMode setting is enabled, otherwise it will change to Crop. Required scope: “layer/resize”

  • id: The block to update.
  • value: The new width of the block.
  • maintainCrop: Whether or not the crop values, if available, should be automatically adjusted.
public func setWidthMode(_ id: DesignBlockID, mode: SizeMode) throws

Set a block’s mode for its width. Required scope: “layer/resize”

  • id: The block to update.
  • mode: The width mode.
public func setHeight(_ id: DesignBlockID, value: Float, maintainCrop: Bool = false) throws

Update a block’s height and optionally maintain the crop. If the crop is maintained, the crop values will be automatically adjusted. The content fill mode Cover is only kept if the features/transformEditsRetainCoverMode setting is enabled, otherwise it will change to Crop. Required scope: “layer/resize”

  • id: The block to update.
  • value: The new height of the block.
  • maintainCrop: Whether or not the crop values, if available, should be automatically adjusted. features/transformEditsRetainCoverMode is enabled.
public func setHeightMode(_ id: DesignBlockID, mode: SizeMode) throws

Set a block’s mode for its height. Required scope: “layer/resize”

  • id: The block to update.
  • mode: The height mode.

Rotation

public func getRotation(_ id: DesignBlockID) throws -> Float

Query a block’s rotation in radians.

  • id:: The block to query.
  • Returns: The block’s rotation around its center in radians.
public func setRotation(_ id: DesignBlockID, radians: Float) throws

Update a block’s rotation. Required scope: “layer/rotate”

  • id: The block to update.
  • radians: The new rotation in radians. Rotation is applied around the block’s center.

Flipping

public func setFlipHorizontal(_ id: DesignBlockID, flip: Bool) throws

Update a block’s horizontal flip. Required scope: “layer/flip”

  • id: The block to update.
  • flip: If the flip should be enabled.
public func getFlipHorizontal(_ id: DesignBlockID) throws -> Bool

Query a block’s horizontal flip state.

  • id:: The block to query.
  • Returns: A boolean indicating for whether the block is flipped in the queried direction.
public func setFlipVertical(_ id: DesignBlockID, flip: Bool) throws

Update a block’s vertical flip. Required scope: “layer/flip”

  • id: The block to update.
  • flip: If the flip should be enabled.
public func getFlipVertical(_ id: DesignBlockID) throws -> Bool

Query a block’s vertical flip state.

  • id:: The block to query.
  • Returns: A boolean indicating for whether the block is flipped in the queried direction.

Scaling

public func scale(_ id: DesignBlockID, to scale: Float, anchorX: Float = 0, anchorY: Float = 0) throws

Scales the block and all of its children proportionally around the specified relative anchor point. This updates the position, size and style properties (e.g. stroke width) of the block and its children. Required scope: “layer/resize”

  • id: The block that should be scaled.
  • scale: The scale factor to be applied to the current properties of the block.
  • anchorX: The relative position along the width of the block around which the scaling should occur. (0 = left edge, 0.5 = center, 1 = right edge)
  • anchorY: The relative position along the height of the block around which the scaling should occur. (0 = top edge, 0.5 = center, 1 = bottom edge)

Fill a Block’s Parent

public func fillParent(_ id: DesignBlockID) throws

Resize and position a block to entirely fill its parent block. The crop values of the block, except for the flip and crop rotation, are reset if it can be cropped. If the size of the block’s fill is unknown, the content fill mode is changed from Crop to Cover to prevent invalid crop values. Required scope: “layer/move”

  • “layer/resize”
  • id:: The block that should fill its parent.

Resize Blocks Content-aware

public func resizeContentAware(_ ids: [DesignBlockID], width: Float, height: Float) throws

Resize all blocks to the given size. The content of the blocks is automatically adjusted to fit the new dimensions. Required scope: “layer/resize”

  • ids: The blocks to resize.
  • width: The new width of the blocks.
  • height: The new height of the blocks.
  • Returns: An error if the blocks could not be resized.

Even Distribution

public func isDistributable(_ ids: [DesignBlockID]) throws -> Bool

Confirms that a given set of blocks can be distributed.

  • ids:: A non-empty array of block ids.
  • Returns: Whether the blocks can be distributed.
public func distributeHorizontally(_ ids: [DesignBlockID]) throws

Distribute multiple blocks horizontally within their bounding box so that the space between them is even. Required scope: “layer/move”

  • ids:: A non-empty array of block ids.
public func distributeVertically(_ ids: [DesignBlockID]) throws

Distribute multiple blocks vertically within their bounding box so that the space between them is even. Required scope: “layer/move”

  • ids:: A non-empty array of block ids.

Alignment

public func isAlignable(_ ids: [DesignBlockID]) throws -> Bool

Confirms that a given set of blocks can be aligned.

  • ids:: A non-empty array of block ids.
  • Returns: Whether the blocks can be aligned.
public func alignHorizontally(_ ids: [DesignBlockID], alignment: HorizontalBlockAlignment) throws

Align multiple blocks horizontally within their bounding box or a single block to its parent. Required scope: “layer/move”

  • ids:: A non-empty array of block ids.
  • alignment:: How they should be aligned: left, right, or center
public func alignVertically(_ ids: [DesignBlockID], alignment: VerticalBlockAlignment) throws

Align multiple blocks vertically within their bounding box or a single block to its parent. Required scope: “layer/move”

  • ids:: A non-empty array of block ids.
  • alignment:: How they should be aligned: top, bottom, or center

Computed Dimensions

public func getFrameX(_ id: DesignBlockID) throws -> Float

Get a block’s layout position on the x-axis. The layout position is only available after an internal update loop, which may not happen immediately.

  • id:: The block to query.
  • Returns: The layout position on the x-axis.
public func getFrameY(_ id: DesignBlockID) throws -> Float

Get a block’s layout position on the y-axis. The layout position is only available after an internal update loop, which may not happen immediately.

  • id:: The block to query.
  • Returns: The layout position on the y-axis.
public func getFrameWidth(_ id: DesignBlockID) throws -> Float

Get a block’s layout width. The layout width is only available after an internal update loop, which may not happen immediately.

  • id:: The block to query.
  • Returns: The layout width.
public func getFrameHeight(_ id: DesignBlockID) throws -> Float

Get a block’s layout height. The layout height is only available after an internal update loop, which may not happen immediately.

  • id:: The block to query.
  • Returns: The layout height.
public func getGlobalBoundingBoxX(_ id: DesignBlockID) throws -> Float

Get the x position of the block’s axis-aligned bounding box in the scene’s global coordinate space. The scene’s global coordinate space has its origin at the top left.

  • id:: The block whose bounding box should be calculated.
  • Returns: The x coordinate of the position of the axis-aligned bounding box.
public func getGlobalBoundingBoxY(_ id: DesignBlockID) throws -> Float

Get the y position of the block’s axis-aligned bounding box in the scene’s global coordinate space. The scene’s global coordinate space has its origin at the top left.

  • id:The: block whose bounding box should be calculated.
  • Returns: The y coordinate of the position of the axis-aligned bounding box.
public func getGlobalBoundingBoxWidth(_ id: DesignBlockID) throws -> Float

Get the width of the block’s axis-aligned bounding box in the scene’s global coordinate space. The scene’s global coordinate space has its origin at the top left.

  • id:: The block whose bounding box should be calculated.
  • Returns: The width of the axis-aligned bounding box.
public func getGlobalBoundingBoxHeight(_ id: DesignBlockID) throws -> Float

Get the height of the block’s axis-aligned bounding box in the scene’s global coordinate space. The scene’s global coordinate space has its origin at the top left.

  • id:: The block whose bounding box should be calculated.
  • Returns: The height of the axis-aligned bounding box.
public func getScreenSpaceBoundingBox(containing blocks: [DesignBlockID]) throws -> CGRect

Get the position and size of the axis-aligned bounding box for the given blocks in screen space.

  • blocks:: The blocks whose bounding box should be calculated.
  • Returns: The position and size of the bounding box as CGRect (in points).

Transform Locking

You can lock the transform of a block to prevent changes to any of its transformations. That is the block’s position, rotation, scale, and sizing.

public func isTransformLocked(_ id: DesignBlockID) throws -> Bool

Query a block’s transform locked state. If true, the block’s transform can’t be changed.

  • id:: The block to query.
  • Returns: True if transform locked, false otherwise.
public func setTransformLocked(_ id: DesignBlockID, locked: Bool) throws

Update a block’s transform locked state.

  • id: The block to update.
  • locked: Whether the block’s transform should be locked.

Full Code

Here’s the full code:

let x = try engine.block.getPositionX(block)
let xMode = try engine.block.getPositionXMode(block)
let y = try engine.block.getPositionY(block)
let yMode = try engine.block.getPositionYMode(block)
try engine.block.setPositionX(block, value: 0.25)
try engine.block.setPositionXMode(block, mode: .percent)
try engine.block.setPositionY(block, value: 0.25)
try engine.block.setPositionYMode(block, mode: .percent)
let rad = try engine.block.getRotation(block)
try engine.block.setRotation(block, radians: .pi)
let flipHorizontal = try engine.block.getFlipHorizontal(block)
let flipVertical = try engine.block.getFlipVertical(block)
try engine.block.setFlipHorizontal(block, flip: true)
try engine.block.setFlipVertical(block, flip: false)
let width = try engine.block.getWidth(block)
let widthMode = try engine.block.getWidthMode(block)
let height = try engine.block.getHeight(block)
let heightMode = try engine.block.getHeightMode(block)
try engine.block.setWidth(block, value: 0.5)
try engine.block.setWidth(block, value: 2.5, maintainCrop: true)
try engine.block.setWidthMode(block, mode: .percent)
try engine.block.setHeight(block, value: 0.5)
try engine.block.setHeight(block, value: 2.5, maintainCrop: true)
try engine.block.setHeightMode(block, mode: .percent)
let frameX = try engine.block.getFrameX(block)
let frameY = try engine.block.getFrameY(block)
let frameWidth = try engine.block.getFrameWidth(block)
let frameHeight = try engine.block.getFrameHeight(block)
try engine.block.setAlwaysOnTop(block, enabled: false)
let isAlwaysOnTop = try engine.block.isAlwaysOnTop(block)
try engine.block.setAlwaysOnBottom(block, enabled: false)
let isAlwaysOnBottom = try engine.block.isAlwaysOnBottom(block)
try engine.block.bringToFront(block)
try engine.block.sendToBack(block)
try engine.block.bringForward(block)
try engine.block.sendBackward(block)
let globalX = try engine.block.getGlobalBoundingBoxX(block)
let globalY = try engine.block.getGlobalBoundingBoxY(block)
let globalWidth = try engine.block.getGlobalBoundingBoxWidth(block)
let globalHeight = try engine.block.getGlobalBoundingBoxHeight(block)
let screenSpaceRect = try engine.block.getScreenSpaceBoundingBox(containing: [block])
try engine.block.scale(block, to: 2.0, anchorX: 0.5, anchorY: 0.5)
try engine.block.scale(block, to: 2.0, anchorX: 0.5, anchorY: 0.5)
try engine.block.fillParent(block)
let pages = try engine.scene.getPages()
try engine.block.resizeContentAware(pages, width: 100.0, height: 100.0)
// Create blocks and append to scene
let member1 = try engine.block.create(.graphic)
let member2 = try engine.block.create(.graphic)
try engine.block.appendChild(to: scene, child: member1)
try engine.block.appendChild(to: scene, child: member2)
if try engine.block.isDistributable([member1, member2]) {
try engine.block.distributeHorizontally([member1, member2])
try engine.block.distributeVertically([member1, member2])
}
if try engine.block.isAlignable([member1, member2]) {
try engine.block.alignHorizontally([member1, member2], alignment: .left)
try engine.block.alignVertically([member1, member2], alignment: .top)
}
let isTransformLocked = try engine.block.isTransformLocked(block)
if !isTransformLocked {
try engine.block.setTransformLocked(block, locked: true)
}