├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── Align │ └── Align.swift └── Tests ├── AlignTests ├── AlignedTests.swift └── XCTestManifests.swift └── LinuxMain.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jacob Bartlett 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Align", 7 | platforms: [ 8 | .macOS(.v10_14), .iOS(.v13), .tvOS(.v13) 9 | ], 10 | products: [ 11 | .library( 12 | name: "Align", 13 | targets: ["Align"]), 14 | ], 15 | dependencies: [], 16 | targets: [ 17 | .target( 18 | name: "Align", 19 | dependencies: []), 20 | .testTarget( 21 | name: "AlignTests", 22 | dependencies: ["Align"]), 23 | ] 24 | ) 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Align 2 | 3 | Align gives you an easy way to align views in SwiftUI. 4 | 5 | Simply `import Align` in whichever SwiftUI file you like and you're good to go. Align supplies the simple `align` ViewModifier which you can add to any view. 6 | 7 | To put your view on the leading edge, it's as simple as: 8 | ``` 9 | Text("Hello, world!").align(.leading) 10 | ``` 11 | 12 | There are 6 different alignments provided by the `ViewAlignment` enum: 13 | ``` 14 | leading 15 | centerX 16 | trailing 17 | top 18 | centerY 19 | bottom 20 | ``` 21 | 22 | The implementation is quite simple - the `.align()` modifier applies a frame modifier with max width/height set to infinity (taking up all available space) and alignment set as part of this frame modifier. 23 | 24 | Check out `Sources/Align/Align.swift` for the source code and to see SwiftUI previews for each alignment. 25 | 26 | My recommendation would be to just copy the source code directly into own project to avoid littering your code with `import Align`. If you do, I'd really appreciate a star on Github! 27 | 28 | Pull requests and constructive criticism welcome. 29 | 30 | Contributors: 31 | - Jacob Bartlett (original author) 32 | - u/lyinsteve who suggested an improvement to the alignment implementation 33 | -------------------------------------------------------------------------------- /Sources/Align/Align.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | public enum ViewAlignment { 4 | case leading 5 | case centerX 6 | case trailing 7 | case top 8 | case centerY 9 | case bottom 10 | } 11 | 12 | public extension View { 13 | 14 | /// The `align` modifier allows you to horizontally and vertically align views using a simple syntax 15 | /// 16 | func align(_ alignment: ViewAlignment) -> some View { 17 | Group { 18 | if alignment == .leading { 19 | frame(maxWidth: .infinity, alignment: .leading) 20 | } 21 | 22 | if alignment == .centerX { 23 | frame(maxWidth: .infinity, alignment: .center) 24 | } 25 | 26 | if alignment == .trailing { 27 | frame(maxWidth: .infinity, alignment: .trailing) 28 | } 29 | 30 | if alignment == .top { 31 | frame(maxHeight: .infinity, alignment: .top) 32 | } 33 | 34 | if alignment == .centerY { 35 | frame(maxHeight: .infinity, alignment: .center) 36 | } 37 | 38 | if alignment == .bottom { 39 | frame(maxHeight: .infinity, alignment: .bottom) 40 | } 41 | } 42 | } 43 | } 44 | 45 | 46 | struct AlignDemoView: View { 47 | 48 | var alignment: ViewAlignment 49 | 50 | var body: some View { 51 | Text("Hello, World!").align(alignment) 52 | } 53 | } 54 | 55 | 56 | struct AlignDemoView_Previews: PreviewProvider { 57 | 58 | private static var alignments: [(name: String, alignment: ViewAlignment)] = [ 59 | (name: "Leading", alignment: .leading), 60 | (name: "CenterX", alignment: .centerX), 61 | (name: "Trailing", alignment: .trailing), 62 | (name: "Top", alignment: .top), 63 | (name: "CenterY", alignment: .centerY), 64 | (name: "Bottom", alignment: .bottom), 65 | ] 66 | 67 | static var previews: some View { 68 | ForEach(alignments, id: \.name) { 69 | AlignDemoView(alignment: $0.alignment) 70 | .previewDisplayName($0.name) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Tests/AlignTests/AlignedTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftUI 3 | @testable import Align 4 | 5 | final class AlignTests: XCTestCase { 6 | 7 | func testLeft() { 8 | XCTAssertNotNil(text.align(.leading)) 9 | } 10 | 11 | func testCenterX() { 12 | XCTAssertNotNil(text.align(.centerX)) 13 | } 14 | 15 | func testRight() { 16 | XCTAssertNotNil(text.align(.trailing)) 17 | } 18 | 19 | func testTop() { 20 | XCTAssertNotNil(text.align(.top)) 21 | } 22 | 23 | func testCenterY() { 24 | XCTAssertNotNil(text.align(.centerY)) 25 | } 26 | 27 | func testBottom() { 28 | XCTAssertNotNil(text.align(.bottom)) 29 | } 30 | 31 | private var text: some View { 32 | Text("Hello, world!") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tests/AlignTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(AlignTests.allTests), 7 | ] 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import AlignTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += AlignTests.allTests() 7 | XCTMain(tests) 8 | --------------------------------------------------------------------------------