├── .swift-version ├── JustUiKitDemo ├── JustUiKit.framework │ ├── .swift-version │ ├── JustUiKit │ ├── Info.plist │ ├── Modules │ │ ├── JustUiKit.swiftmodule │ │ │ ├── i386.swiftdoc │ │ │ └── i386.swiftmodule │ │ └── module.modulemap │ ├── Headers │ │ ├── JustUiKit.h │ │ └── JustUiKit-Swift.h │ ├── JustUiKit.podspec │ └── _CodeSignature │ │ └── CodeResources ├── ViewController.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── Base.lproj │ ├── Main.storyboard │ └── LaunchScreen.storyboard └── AppDelegate.swift ├── art ├── framelayout1.png ├── framelayout2.png ├── relativelayout1.png ├── vertical_layout.png └── horizontal_layout.png ├── JustUiKit ├── JustUiKit.xcodeproj.xcworkspace │ └── contents.xcworkspacedata ├── utils │ ├── ReleasePool.swift │ ├── Orientation.swift │ ├── ColorExtension.swift │ ├── Queue.swift │ └── Gravity.swift ├── JustUiKit.h ├── Info.plist ├── layout │ ├── JustViewManager.swift │ ├── JustViewParent.swift │ ├── JustViewGroup.swift │ └── JustExtensionView.swift ├── params │ └── JustLayoutParams.swift └── widget │ ├── JustFrameLayout.swift │ ├── JustLinearLayout.swift │ └── JustRelativeLayout.swift ├── JustUiKitTests ├── Info.plist ├── JustUiKitTests.swift └── ViewController.swift ├── LICENSE ├── JustUiKitSample └── JustUiKitSample │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Info.plist │ ├── AppDelegate.swift │ ├── Base.lproj │ └── LaunchScreen.storyboard │ └── ViewController.swift ├── .gitignore ├── JustUiKit.podspec ├── README_ZH.md ├── README.md └── JustUiKit.xcodeproj └── project.pbxproj /.swift-version: -------------------------------------------------------------------------------- 1 | 3.0 -------------------------------------------------------------------------------- /JustUiKitDemo/JustUiKit.framework/.swift-version: -------------------------------------------------------------------------------- 1 | 3.0 -------------------------------------------------------------------------------- /art/framelayout1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfkdsk/JustUiKit/HEAD/art/framelayout1.png -------------------------------------------------------------------------------- /art/framelayout2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfkdsk/JustUiKit/HEAD/art/framelayout2.png -------------------------------------------------------------------------------- /art/relativelayout1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfkdsk/JustUiKit/HEAD/art/relativelayout1.png -------------------------------------------------------------------------------- /art/vertical_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfkdsk/JustUiKit/HEAD/art/vertical_layout.png -------------------------------------------------------------------------------- /art/horizontal_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfkdsk/JustUiKit/HEAD/art/horizontal_layout.png -------------------------------------------------------------------------------- /JustUiKitDemo/JustUiKit.framework/JustUiKit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfkdsk/JustUiKit/HEAD/JustUiKitDemo/JustUiKit.framework/JustUiKit -------------------------------------------------------------------------------- /JustUiKitDemo/JustUiKit.framework/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfkdsk/JustUiKit/HEAD/JustUiKitDemo/JustUiKit.framework/Info.plist -------------------------------------------------------------------------------- /JustUiKitDemo/JustUiKit.framework/Modules/JustUiKit.swiftmodule/i386.swiftdoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfkdsk/JustUiKit/HEAD/JustUiKitDemo/JustUiKit.framework/Modules/JustUiKit.swiftmodule/i386.swiftdoc -------------------------------------------------------------------------------- /JustUiKitDemo/JustUiKit.framework/Modules/JustUiKit.swiftmodule/i386.swiftmodule: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfkdsk/JustUiKit/HEAD/JustUiKitDemo/JustUiKit.framework/Modules/JustUiKit.swiftmodule/i386.swiftmodule -------------------------------------------------------------------------------- /JustUiKit/JustUiKit.xcodeproj.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /JustUiKitDemo/JustUiKit.framework/Modules/module.modulemap: -------------------------------------------------------------------------------- 1 | framework module JustUiKit { 2 | umbrella header "JustUiKit.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | 8 | module JustUiKit.Swift { 9 | header "JustUiKit-Swift.h" 10 | } 11 | -------------------------------------------------------------------------------- /JustUiKit/utils/ReleasePool.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by 刘丰恺 on 2017/1/24. 3 | // Copyright (c) 2017 ___FULL___. All rights reserved. 4 | // 5 | 6 | import Foundation 7 | 8 | public class ReleasePool { 9 | var releasePool: Array = Array() 10 | 11 | public func acquire() -> T? { 12 | if releasePool.count > 0 { 13 | let obj = releasePool.removeLast() 14 | return obj 15 | } 16 | return nil 17 | } 18 | 19 | public func release(instance: T) { 20 | releasePool.append(instance) 21 | } 22 | } -------------------------------------------------------------------------------- /JustUiKit/JustUiKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // JustUiKit.h 3 | // JustUiKit 4 | // 5 | // Created by 刘丰恺 on 2017/1/17. 6 | // Copyright (c) 2017 ___lfkdsk___. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | //! Project version number for JustUiKit. 13 | FOUNDATION_EXPORT double JustUiKitVersionNumber; 14 | 15 | //! Project version string for JustUiKit. 16 | FOUNDATION_EXPORT const unsigned char JustUiKitVersionString[]; 17 | 18 | // In this header, you should import all the public headers of your framework using statements like #import 19 | 20 | -------------------------------------------------------------------------------- /JustUiKitDemo/JustUiKit.framework/Headers/JustUiKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // JustUiKit.h 3 | // JustUiKit 4 | // 5 | // Created by 刘丰恺 on 2017/1/17. 6 | // Copyright (c) 2017 ___lfkdsk___. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | //! Project version number for JustUiKit. 13 | FOUNDATION_EXPORT double JustUiKitVersionNumber; 14 | 15 | //! Project version string for JustUiKit. 16 | FOUNDATION_EXPORT const unsigned char JustUiKitVersionString[]; 17 | 18 | // In this header, you should import all the public headers of your framework using statements like #import 19 | 20 | -------------------------------------------------------------------------------- /JustUiKitDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // JustUiKitDemo 4 | // 5 | // Created by 刘丰恺 on 2017/1/18. 6 | // Copyright © 2017年 ___lfkdsk___. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /JustUiKitTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /JustUiKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 JustWe 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 | -------------------------------------------------------------------------------- /JustUiKitTests/JustUiKitTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JustUiKitTests.swift 3 | // JustUiKitTests 4 | // 5 | // Created by 刘丰恺 on 2017/1/17. 6 | // Copyright (c) 2017 ___lfkdsk___. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import JustUiKit 11 | 12 | class JustUiKitTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | var controller = ViewController(coder: NSCoder()) 26 | // This is an example of a functional test case. 27 | // Use XCTAssert and related functions to verify your tests produce the correct results. 28 | } 29 | 30 | func testPerformanceExample() { 31 | // This is an example of a performance test case. 32 | self.measure { 33 | // Put the code you want to measure the time of here. 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /JustUiKit/utils/Orientation.swift: -------------------------------------------------------------------------------- 1 | /// MIT License 2 | /// 3 | /// Copyright (c) 2017 JustWe 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 | 23 | 24 | public enum Orientation { 25 | case Vertical 26 | case Horizontal 27 | } -------------------------------------------------------------------------------- /JustUiKitDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /JustUiKitSample/JustUiKitSample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /JustUiKit/layout/JustViewManager.swift: -------------------------------------------------------------------------------- 1 | /// MIT License 2 | /// 3 | /// Copyright (c) 2017 JustWe 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 | 23 | 24 | import UIKit 25 | 26 | public protocol JustViewManager { 27 | 28 | func addView(view: UIView) 29 | 30 | func updateView(view: UIView, 31 | params: LayoutParams) 32 | 33 | func removeView(view: UIView) 34 | } -------------------------------------------------------------------------------- /JustUiKit/layout/JustViewParent.swift: -------------------------------------------------------------------------------- 1 | /// MIT License 2 | /// 3 | /// Copyright (c) 2017 JustWe 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 | 23 | import Foundation 24 | import UIKit 25 | 26 | public protocol JustViewParent { 27 | func getParent() -> JustViewParent 28 | 29 | func requestLayout() 30 | 31 | func setParent(viewGroup: JustViewParent) 32 | 33 | func hasParent() -> Bool 34 | } 35 | -------------------------------------------------------------------------------- /JustUiKitSample/JustUiKitSample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /JustUiKitDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /JustUiKit/utils/ColorExtension.swift: -------------------------------------------------------------------------------- 1 | /// MIT License 2 | /// 3 | /// Copyright (c) 2017 JustWe 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 | 23 | import UIKit 24 | 25 | extension UIColor { 26 | public convenience init(rgb: UInt, alphaVal: CGFloat? = 1.0) { 27 | self.init( 28 | red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0, 29 | green: CGFloat((rgb & 0x00FF00) >> 8) / 255.0, 30 | blue: CGFloat(rgb & 0x0000FF) / 255.0, 31 | alpha: CGFloat(alphaVal!) 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | .idea/ 6 | idea/ 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xcuserstate 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /JustUiKitDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /JustUiKitDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /JustUiKitDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // JustUiKitDemo 4 | // 5 | // Created by 刘丰恺 on 2017/1/18. 6 | // Copyright © 2017年 ___lfkdsk___. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /JustUiKit/utils/Queue.swift: -------------------------------------------------------------------------------- 1 | /// MIT License 2 | /// 3 | /// Copyright (c) 2017 JustWe 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 | 23 | public struct Queue { 24 | fileprivate var array = [T?]() 25 | fileprivate var head = 0 26 | 27 | public var isEmpty: Bool { 28 | return count == 0 29 | } 30 | 31 | public var count: Int { 32 | return array.count - head 33 | } 34 | 35 | public mutating func enqueue(_ element: T) { 36 | array.append(element) 37 | } 38 | 39 | public mutating func dequeue() -> T? { 40 | guard head < array.count, let element = array[head] else { 41 | return nil 42 | } 43 | 44 | array[head] = nil 45 | head += 1 46 | 47 | let percentage = Double(head) / Double(array.count) 48 | if array.count > 50 && percentage > 0.25 { 49 | array.removeFirst(head) 50 | head = 0 51 | } 52 | 53 | return element 54 | } 55 | 56 | public var front: T? { 57 | if isEmpty { 58 | return nil 59 | } else { 60 | return array[head] 61 | } 62 | } 63 | 64 | public mutating func clear() { 65 | array.removeAll() 66 | head = 0 67 | } 68 | } -------------------------------------------------------------------------------- /JustUiKitSample/JustUiKitSample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // JustUiKitSample 4 | // 5 | // Created by 刘丰恺 on 2017/2/6. 6 | // Copyright (c) 2017 ___lfkdsk___. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | self.window = UIWindow(frame: UIScreen.main.bounds) 20 | 21 | let mainViewController = ViewController() 22 | self.window?.rootViewController = mainViewController 23 | self.window?.makeKeyAndVisible() 24 | return true 25 | } 26 | 27 | 28 | func applicationWillResignActive(_ application: UIApplication) { 29 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 30 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 31 | 32 | } 33 | 34 | 35 | func applicationDidEnterBackground(_ application: UIApplication) { 36 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 37 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 38 | 39 | } 40 | 41 | 42 | func applicationWillEnterForeground(_ application: UIApplication) { 43 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 44 | } 45 | 46 | 47 | func applicationDidBecomeActive(_ application: UIApplication) { 48 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 49 | } 50 | 51 | 52 | func applicationWillTerminate(_ application: UIApplication) { 53 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /JustUiKit/utils/Gravity.swift: -------------------------------------------------------------------------------- 1 | /// MIT License 2 | /// 3 | /// Copyright (c) 2017 JustWe 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 | 23 | 24 | public class Gravity { 25 | static let VERTICAL_GRAVITY_MASK = 112 26 | static let HORIZONTAL_GRAVITY_MASK = 7 27 | 28 | private static let NO_GRAVITY_V = 0 29 | private static let TOP_V = 48 30 | private static let BOTTOM_V = 80 31 | private static let LEFT_V = 3 32 | private static let RIGHT_V = 5 33 | private static let CENTER_VERTICAL_V = 16 34 | private static let CENTER_HORIZONTAL_V = 1 35 | private static let CENTER_V = 17 36 | 37 | public static let NO_GRAVITY = Gravity(value: NO_GRAVITY_V) 38 | public static let TOP = Gravity(value: TOP_V) 39 | public static let BOTTOM = Gravity(value: BOTTOM_V) 40 | public static let LEFT = Gravity(value: LEFT_V) 41 | public static let RIGHT = Gravity(value: RIGHT_V) 42 | public static let CENTER_VERTICAL = Gravity(value: CENTER_VERTICAL_V) 43 | public static let CENTER_HORIZONTAL = Gravity(value: CENTER_HORIZONTAL_V) 44 | public static let CENTER = Gravity(value: CENTER_V) 45 | 46 | private let innerValue: Int 47 | 48 | private init(value: Int) { 49 | self.innerValue = value 50 | } 51 | 52 | public static func |(left: Gravity, right: Gravity) -> Gravity { 53 | return Gravity(value: left.getValue() | right.getValue()) 54 | } 55 | 56 | public static func getHorizontalGravity(gravity: Gravity) -> Int { 57 | return gravity.getValue() & HORIZONTAL_GRAVITY_MASK 58 | } 59 | 60 | public static func getVerticalGravity(gravity: Gravity) -> Int { 61 | return gravity.getValue() & VERTICAL_GRAVITY_MASK 62 | } 63 | 64 | public func getValue() -> Int { 65 | return innerValue 66 | } 67 | } -------------------------------------------------------------------------------- /JustUiKitSample/JustUiKitSample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /JustUiKit/params/JustLayoutParams.swift: -------------------------------------------------------------------------------- 1 | /// MIT License 2 | /// 3 | /// Copyright (c) 2017 JustWe 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 | 23 | 24 | import UIKit 25 | 26 | public class LayoutParams { 27 | 28 | public static let MATCH_PARENT: CGFloat = -1 29 | public static let WRAP_CONTENT: CGFloat = -2 30 | 31 | public var width: CGFloat = 0 32 | public var height: CGFloat = 0 33 | 34 | public var bindView: UIView? = nil 35 | 36 | public init(width: CGFloat, height: CGFloat) { 37 | self.width = width 38 | self.height = height 39 | } 40 | 41 | public func bindWith(view: UIView?) { 42 | self.bindView = view 43 | } 44 | 45 | public init(_ params: LayoutParams) { 46 | self.width = params.width 47 | self.height = params.height 48 | } 49 | 50 | public static func generateDefaultLayoutParams() -> LayoutParams { 51 | return LayoutParams(width: 0, height: 0) 52 | } 53 | } 54 | 55 | public class MarginLayoutParams: LayoutParams { 56 | private static let DEFAULT_MARGIN: Int = 0; 57 | 58 | public var leftMargin = DEFAULT_MARGIN 59 | public var rightMargin = DEFAULT_MARGIN 60 | public var topMargin = DEFAULT_MARGIN 61 | public var bottomMargin = DEFAULT_MARGIN 62 | 63 | public var startMargin = DEFAULT_MARGIN 64 | public var endMargin = DEFAULT_MARGIN 65 | 66 | override public init(_ source: LayoutParams) { 67 | super.init(source) 68 | } 69 | 70 | override public init(width: CGFloat, height: CGFloat) { 71 | super.init(width: width, height: height) 72 | } 73 | 74 | public init(source: MarginLayoutParams) { 75 | super.init(width: source.width, height: source.height) 76 | self.leftMargin = source.leftMargin 77 | self.rightMargin = source.rightMargin 78 | self.topMargin = source.topMargin 79 | self.bottomMargin = source.bottomMargin 80 | } 81 | 82 | public func setMargins(left: Int, top: Int, right: Int, bottom: Int) { 83 | leftMargin = left; 84 | topMargin = top; 85 | rightMargin = right; 86 | bottomMargin = bottom; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /JustUiKit/layout/JustViewGroup.swift: -------------------------------------------------------------------------------- 1 | /// MIT License 2 | /// 3 | /// Copyright (c) 2017 JustWe 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 | 23 | import Foundation 24 | import UIKit 25 | 26 | open class JustViewGroup: UIView, JustViewParent, JustViewManager { 27 | 28 | private var parentView: JustViewParent? = nil; 29 | 30 | private var isChanged: Bool = false 31 | 32 | public init() { 33 | super.init(frame: CGRect.zero) 34 | isChanged = false 35 | } 36 | 37 | public override init(frame groupFrame: CGRect) { 38 | super.init(frame: groupFrame) 39 | isChanged = false 40 | } 41 | 42 | public init(frame groupFrame: CGRect, 43 | parentView: JustViewParent) { 44 | super.init(frame: groupFrame) 45 | self.parentView = parentView 46 | } 47 | 48 | public required init?(coder aDecoder: NSCoder) { 49 | super.init(coder: aDecoder) 50 | } 51 | 52 | public func onLayout(_ changed: Bool = false, 53 | _ top: CGFloat, 54 | _ left: CGFloat, 55 | _ right: CGFloat, 56 | _ bottom: CGFloat) { 57 | 58 | } 59 | 60 | public func onMeasure(_ size: CGSize) { 61 | 62 | } 63 | 64 | override open func layoutSubviews() { 65 | 66 | let top = frame.origin.y 67 | let left = frame.origin.x 68 | let right = frame.size.width + left 69 | let bottom = frame.size.height + top 70 | 71 | onMeasure(frame.size) 72 | 73 | onLayout(isChanged, top, left, right, bottom) 74 | } 75 | 76 | public func getParent() -> JustViewParent { 77 | return parentView! 78 | } 79 | 80 | public func requestLayout() { 81 | 82 | } 83 | 84 | public func setParent(viewGroup: JustViewParent) { 85 | self.parentView = viewGroup 86 | } 87 | 88 | 89 | public func hasParent() -> Bool { 90 | return parentView != nil 91 | } 92 | 93 | public func updateView(view: UIView, params: LayoutParams) { 94 | 95 | } 96 | 97 | public func removeView(view: UIView) { 98 | // self.willRemoveSubview(view) 99 | } 100 | 101 | public func addView(view: UIView) { 102 | 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /JustUiKitTests/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // JustUiKitTest 4 | // 5 | // Created by 刘丰恺 on 2017/1/2. 6 | // Copyright (c) 2017 ___FULL___. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | class ViewController: UIViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | // Do any additional setup after loading the view, typically from a nib. 17 | } 18 | 19 | 20 | override func didReceiveMemoryWarning() { 21 | super.didReceiveMemoryWarning() 22 | // Dispose of any resources that can be recreated. 23 | } 24 | 25 | func createSectionLabel(_ title: String) -> UILabel { 26 | let sectionLabel = UILabel() 27 | sectionLabel.text = title; 28 | sectionLabel.font = UIFont.systemFont(ofSize: 17) 29 | sectionLabel.sizeToFit() 30 | // sizeToFit函数的意思是让视图的尺寸刚好包裹其内容。 31 | // 注意sizeToFit方法必要在设置字体、文字后调用才正确。 32 | return sectionLabel 33 | } 34 | 35 | 36 | override func loadView() { 37 | super.loadView() 38 | 39 | self.view = UIView(frame: UIScreen.main.bounds) 40 | view.backgroundColor = UIColor(rgb: 0xFF8C00) 41 | 42 | let lfkdsk = JustLinearLayout(frame: UIScreen.main.bounds, orientation: .Horizontal) 43 | 44 | view.addSubview(lfkdsk) 45 | 46 | let linearLayout: JustLinearLayout = lfkdsk 47 | 48 | let params: LinearLayoutParams = LinearLayoutParams( 49 | width: LayoutParams.WRAP_CONTENT, 50 | height: LayoutParams.WRAP_CONTENT) 51 | params.weight = 1 52 | // params.leftMargin = 100 53 | // params.rightMargin = 10 54 | 55 | let fuckView = createSectionLabel("FUCK") 56 | 57 | print(fuckView.frame.origin) 58 | print(fuckView.frame.size) 59 | 60 | print("Before Layout") 61 | 62 | let centerParams = LinearLayoutParams(linear: params) 63 | // centerParams.weight = 1 64 | centerParams.layoutGravity = Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL 65 | 66 | let rightParams = LinearLayoutParams(linear: params) 67 | rightParams.layoutGravity = Gravity.BOTTOM.getValue() 68 | 69 | let rightAndBottomParams = LinearLayoutParams(params) 70 | // rightAndBottomParams.weight = 1 71 | rightAndBottomParams.layoutGravity = Gravity.BOTTOM | Gravity.RIGHT 72 | 73 | let rightAndTopParams = LinearLayoutParams(linear: params) 74 | // rightAndTopParams.rightMargin = 12 75 | // rightAndTopParams.weight = 1 76 | rightAndTopParams.layoutGravity = Gravity.TOP | Gravity.RIGHT 77 | 78 | linearLayout.addView(view: createSectionLabel("苟"), params: rightAndTopParams) 79 | linearLayout.addView(view: createSectionLabel("利"), params: params) 80 | linearLayout.addView(view: createSectionLabel("国"), params: params) 81 | // linearLayout.addView(view: createSectionLabel("家"), params: centerParams) 82 | // linearLayout.addView(view: createSectionLabel("生"), params: params) 83 | // linearLayout.addView(view: createSectionLabel("死"), params: rightAndBottomParams) 84 | // linearLayout.addView(view: createSectionLabel("以"), params: params) 85 | // linearLayout.addView(view: createSectionLabel("岂因祸福"), params: params) 86 | // linearLayout.addView(view: createSectionLabel("岂因祸福"), params: params) 87 | // linearLayout.addView(view: createSectionLabel("岂因祸福"), params: params) 88 | // linearLayout.addView(view: createSectionLabel("以"), params: params) 89 | 90 | var fuck2View = JustLinearLayout(orientation: .Horizontal) 91 | // fuck2View.linearExtension.padding.paddingBottom = 10 92 | var Fuck2Params = LinearLayoutParams(width: LayoutParams.MATCH_PARENT, 93 | height: LayoutParams.WRAP_CONTENT) 94 | // Fuck2Params.topMargin = 80 95 | Fuck2Params.leftMargin = 20 96 | Fuck2Params.weight = 1 97 | Fuck2Params.layoutGravity = Gravity.CENTER_VERTICAL.getValue() 98 | // fuck2View.setPadding(top: 10, left: 0, right: 10, bottom: 0) 99 | lfkdsk.addView(view: fuck2View, params: Fuck2Params) 100 | 101 | let firstInnerItem = createSectionLabel("以") 102 | // rightAndTopParams.bottomMargin = 10 103 | 104 | fuck2View.addView(view: firstInnerItem, params: params) 105 | fuck2View.addView(view: createSectionLabel("岂因祸福"), params: params) 106 | fuck2View.addView(view: createSectionLabel("岂因祸福"), params: params) 107 | fuck2View.addView(view: createSectionLabel("岂因祸福"), params: params) 108 | fuck2View.addView(view: createSectionLabel("岂因祸福"), params: rightParams) 109 | fuck2View.addView(view: createSectionLabel("岂因祸福"), params: params) 110 | fuck2View.addView(view: createSectionLabel("岂因祸福"), params: centerParams) 111 | // fuck2View.linearExtension.padding.paddingRight = 80 112 | 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /JustUiKit.podspec: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Be sure to run `pod spec lint TangramKit.podspec' to ensure this is a 4 | # valid spec and to remove all comments including this before submitting the spec. 5 | # 6 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 7 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 8 | # 9 | 10 | Pod::Spec.new do |s| 11 | 12 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 13 | # 14 | # These will help people to find your library, and whilst it 15 | # can feel like a chore to fill in it's definitely to your advantage. The 16 | # summary should be tweet-length, and the description more in depth. 17 | # 18 | 19 | s.name = "JustUiKit" 20 | s.version = "0.2.1" 21 | s.summary = "iOS UI Kit With Android-Style Tools" 22 | 23 | s.description = <<-DESC 24 | *iOS UI Kit With Android-Style Tools. 25 | DESC 26 | 27 | s.homepage = "https://github.com/lfkdsk/JustUiKit" 28 | 29 | 30 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 31 | # 32 | # Licensing your code is important. See http://choosealicense.com for more info. 33 | # CocoaPods will detect a license file if there is a named LICENSE* 34 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 35 | # 36 | 37 | s.license = "MIT" 38 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 39 | 40 | 41 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 42 | # 43 | # Specify the authors of the library, with email addresses. Email addresses 44 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 45 | # accepts just a name if you'd rather not provide an email address. 46 | # 47 | # Specify a social_media_url where others can refer to, for example a twitter 48 | # profile URL. 49 | # 50 | 51 | s.author = { "lfkdsk" => "lfk_dsk@hotmail.com" } 52 | # Or just: s.author = "lfkdsk" 53 | 54 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 55 | # 56 | # If this Pod runs only on iOS or OS X, then specify the platform and 57 | # the deployment target. You can optionally include the target after the platform. 58 | # 59 | 60 | # s.platform = :ios 61 | s.platform = :ios, "8.0" 62 | 63 | # When using multiple platforms 64 | # s.ios.deployment_target = "5.0" 65 | # s.osx.deployment_target = "10.7" 66 | # s.watchos.deployment_target = "2.0" 67 | 68 | 69 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 70 | # 71 | # Specify the location from where the source should be retrieved. 72 | # Supports git, hg, bzr, svn and HTTP. 73 | # 74 | 75 | s.source = { :git => "https://github.com/lfkdsk/JustUiKit.git", :tag => "0.2.1" } 76 | 77 | 78 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 79 | # 80 | # CocoaPods is smart about how it includes source code. For source files 81 | # giving a folder will include any swift, h, m, mm, c & cpp files. 82 | # For header files it will include any header in the folder. 83 | # Not including the public_header_files will make all headers public. 84 | # 85 | 86 | s.source_files = "JustUiKit/**/*.swift" 87 | #s.exclude_files = "Classes/Exclude" 88 | 89 | #s.public_header_files = "JustUiKit/Lib/*.h" 90 | 91 | 92 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 93 | # 94 | # A list of resources included with the Pod. These are copied into the 95 | # target bundle with a build phase script. Anything else will be cleaned. 96 | # You can preserve files from being cleaned, please don't preserve 97 | # non-essential files like tests, examples and documentation. 98 | # 99 | 100 | # s.resource = "icon.png" 101 | # s.resources = "Resources/*.png" 102 | 103 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 104 | 105 | 106 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 107 | # 108 | # Link your library with frameworks, or libraries. Libraries do not include 109 | # the lib prefix of their name. 110 | # 111 | 112 | # s.framework = "SomeFramework" 113 | # s.frameworks = "SomeFramework", "AnotherFramework" 114 | 115 | # s.library = "iconv" 116 | # s.libraries = "iconv", "xml2" 117 | 118 | 119 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 120 | # 121 | # If your library depends on compiler flags you can set them in the xcconfig hash 122 | # where they will only apply to your library. If you depend on other Podspecs 123 | # you can include multiple dependencies to ensure it works. 124 | 125 | # s.requires_arc = true 126 | 127 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 128 | # s.dependency "JSONKit", "~> 1.4" 129 | 130 | end -------------------------------------------------------------------------------- /JustUiKitDemo/JustUiKit.framework/JustUiKit.podspec: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Be sure to run `pod spec lint TangramKit.podspec' to ensure this is a 4 | # valid spec and to remove all comments including this before submitting the spec. 5 | # 6 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 7 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 8 | # 9 | 10 | Pod::Spec.new do |s| 11 | 12 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 13 | # 14 | # These will help people to find your library, and whilst it 15 | # can feel like a chore to fill in it's definitely to your advantage. The 16 | # summary should be tweet-length, and the description more in depth. 17 | # 18 | 19 | s.name = "JustUiKit" 20 | s.version = "0.1.3" 21 | s.summary = "iOS UI Kit With Android-Style Tools" 22 | 23 | s.description = <<-DESC 24 | *iOS UI Kit With Android-Style Tools. 25 | DESC 26 | 27 | s.homepage = "https://github.com/lfkdsk/JustUiKit" 28 | 29 | 30 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 31 | # 32 | # Licensing your code is important. See http://choosealicense.com for more info. 33 | # CocoaPods will detect a license file if there is a named LICENSE* 34 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 35 | # 36 | 37 | s.license = "MIT" 38 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 39 | 40 | 41 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 42 | # 43 | # Specify the authors of the library, with email addresses. Email addresses 44 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 45 | # accepts just a name if you'd rather not provide an email address. 46 | # 47 | # Specify a social_media_url where others can refer to, for example a twitter 48 | # profile URL. 49 | # 50 | 51 | s.author = { "lfkdsk" => "lfk_dsk@hotmail.com" } 52 | # Or just: s.author = "lfkdsk" 53 | 54 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 55 | # 56 | # If this Pod runs only on iOS or OS X, then specify the platform and 57 | # the deployment target. You can optionally include the target after the platform. 58 | # 59 | 60 | # s.platform = :ios 61 | s.platform = :ios, "8.0" 62 | 63 | # When using multiple platforms 64 | # s.ios.deployment_target = "5.0" 65 | # s.osx.deployment_target = "10.7" 66 | # s.watchos.deployment_target = "2.0" 67 | 68 | 69 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 70 | # 71 | # Specify the location from where the source should be retrieved. 72 | # Supports git, hg, bzr, svn and HTTP. 73 | # 74 | 75 | s.source = { :git => "https://github.com/lfkdsk/JustUiKit.git", :tag => "0.1.3" } 76 | 77 | 78 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 79 | # 80 | # CocoaPods is smart about how it includes source code. For source files 81 | # giving a folder will include any swift, h, m, mm, c & cpp files. 82 | # For header files it will include any header in the folder. 83 | # Not including the public_header_files will make all headers public. 84 | # 85 | 86 | s.source_files = "JustUiKit/**/*.swift" 87 | #s.exclude_files = "Classes/Exclude" 88 | 89 | #s.public_header_files = "JustUiKit/Lib/*.h" 90 | 91 | 92 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 93 | # 94 | # A list of resources included with the Pod. These are copied into the 95 | # target bundle with a build phase script. Anything else will be cleaned. 96 | # You can preserve files from being cleaned, please don't preserve 97 | # non-essential files like tests, examples and documentation. 98 | # 99 | 100 | # s.resource = "icon.png" 101 | # s.resources = "Resources/*.png" 102 | 103 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 104 | 105 | 106 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 107 | # 108 | # Link your library with frameworks, or libraries. Libraries do not include 109 | # the lib prefix of their name. 110 | # 111 | 112 | # s.framework = "SomeFramework" 113 | # s.frameworks = "SomeFramework", "AnotherFramework" 114 | 115 | # s.library = "iconv" 116 | # s.libraries = "iconv", "xml2" 117 | 118 | 119 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 120 | # 121 | # If your library depends on compiler flags you can set them in the xcconfig hash 122 | # where they will only apply to your library. If you depend on other Podspecs 123 | # you can include multiple dependencies to ensure it works. 124 | 125 | # s.requires_arc = true 126 | 127 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 128 | # s.dependency "JSONKit", "~> 1.4" 129 | 130 | end -------------------------------------------------------------------------------- /JustUiKitDemo/JustUiKit.framework/Headers/JustUiKit-Swift.h: -------------------------------------------------------------------------------- 1 | // Generated by Apple Swift version 3.0.2 (swiftlang-800.0.63 clang-800.0.42.1) 2 | #pragma clang diagnostic push 3 | 4 | #if defined(__has_include) && __has_include() 5 | # include 6 | #endif 7 | 8 | #pragma clang diagnostic ignored "-Wauto-import" 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #if !defined(SWIFT_TYPEDEFS) 15 | # define SWIFT_TYPEDEFS 1 16 | # if defined(__has_include) && __has_include() 17 | # include 18 | # elif !defined(__cplusplus) || __cplusplus < 201103L 19 | typedef uint_least16_t char16_t; 20 | typedef uint_least32_t char32_t; 21 | # endif 22 | typedef float swift_float2 __attribute__((__ext_vector_type__(2))); 23 | typedef float swift_float3 __attribute__((__ext_vector_type__(3))); 24 | typedef float swift_float4 __attribute__((__ext_vector_type__(4))); 25 | typedef double swift_double2 __attribute__((__ext_vector_type__(2))); 26 | typedef double swift_double3 __attribute__((__ext_vector_type__(3))); 27 | typedef double swift_double4 __attribute__((__ext_vector_type__(4))); 28 | typedef int swift_int2 __attribute__((__ext_vector_type__(2))); 29 | typedef int swift_int3 __attribute__((__ext_vector_type__(3))); 30 | typedef int swift_int4 __attribute__((__ext_vector_type__(4))); 31 | typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2))); 32 | typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3))); 33 | typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4))); 34 | #endif 35 | 36 | #if !defined(SWIFT_PASTE) 37 | # define SWIFT_PASTE_HELPER(x, y) x##y 38 | # define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y) 39 | #endif 40 | #if !defined(SWIFT_METATYPE) 41 | # define SWIFT_METATYPE(X) Class 42 | #endif 43 | #if !defined(SWIFT_CLASS_PROPERTY) 44 | # if __has_feature(objc_class_property) 45 | # define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__ 46 | # else 47 | # define SWIFT_CLASS_PROPERTY(...) 48 | # endif 49 | #endif 50 | 51 | #if defined(__has_attribute) && __has_attribute(objc_runtime_name) 52 | # define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X))) 53 | #else 54 | # define SWIFT_RUNTIME_NAME(X) 55 | #endif 56 | #if defined(__has_attribute) && __has_attribute(swift_name) 57 | # define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X))) 58 | #else 59 | # define SWIFT_COMPILE_NAME(X) 60 | #endif 61 | #if defined(__has_attribute) && __has_attribute(objc_method_family) 62 | # define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X))) 63 | #else 64 | # define SWIFT_METHOD_FAMILY(X) 65 | #endif 66 | #if defined(__has_attribute) && __has_attribute(noescape) 67 | # define SWIFT_NOESCAPE __attribute__((noescape)) 68 | #else 69 | # define SWIFT_NOESCAPE 70 | #endif 71 | #if !defined(SWIFT_CLASS_EXTRA) 72 | # define SWIFT_CLASS_EXTRA 73 | #endif 74 | #if !defined(SWIFT_PROTOCOL_EXTRA) 75 | # define SWIFT_PROTOCOL_EXTRA 76 | #endif 77 | #if !defined(SWIFT_ENUM_EXTRA) 78 | # define SWIFT_ENUM_EXTRA 79 | #endif 80 | #if !defined(SWIFT_CLASS) 81 | # if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted) 82 | # define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA 83 | # define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA 84 | # else 85 | # define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA 86 | # define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA 87 | # endif 88 | #endif 89 | 90 | #if !defined(SWIFT_PROTOCOL) 91 | # define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA 92 | # define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA 93 | #endif 94 | 95 | #if !defined(SWIFT_EXTENSION) 96 | # define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__) 97 | #endif 98 | 99 | #if !defined(OBJC_DESIGNATED_INITIALIZER) 100 | # if defined(__has_attribute) && __has_attribute(objc_designated_initializer) 101 | # define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) 102 | # else 103 | # define OBJC_DESIGNATED_INITIALIZER 104 | # endif 105 | #endif 106 | #if !defined(SWIFT_ENUM) 107 | # define SWIFT_ENUM(_type, _name) enum _name : _type _name; enum SWIFT_ENUM_EXTRA _name : _type 108 | # if defined(__has_feature) && __has_feature(generalized_swift_name) 109 | # define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_EXTRA _name : _type 110 | # else 111 | # define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME) SWIFT_ENUM(_type, _name) 112 | # endif 113 | #endif 114 | #if !defined(SWIFT_UNAVAILABLE) 115 | # define SWIFT_UNAVAILABLE __attribute__((unavailable)) 116 | #endif 117 | #if defined(__has_feature) && __has_feature(modules) 118 | @import UIKit; 119 | @import CoreGraphics; 120 | #endif 121 | 122 | #pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" 123 | #pragma clang diagnostic ignored "-Wduplicate-method-arg" 124 | 125 | @interface UIColor (SWIFT_EXTENSION(JustUiKit)) 126 | @end 127 | 128 | 129 | @interface UIView (SWIFT_EXTENSION(JustUiKit)) 130 | - (void)setPaddingWithTop:(CGFloat)top left:(CGFloat)left right:(CGFloat)right bottom:(CGFloat)bottom; 131 | @end 132 | 133 | #pragma clang diagnostic pop 134 | -------------------------------------------------------------------------------- /JustUiKitDemo/JustUiKit.framework/_CodeSignature/CodeResources: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | files 6 | 7 | .swift-version 8 | 9 | vcFAipHxYdq1qYk9I9s8cJUgDh0= 10 | 11 | Headers/JustUiKit-Swift.h 12 | 13 | q5hJZ57zzllNSYHIjOATlcfsXFs= 14 | 15 | Headers/JustUiKit.h 16 | 17 | apLsWgBOz9JiVcsrWzwvrrqC9XU= 18 | 19 | Info.plist 20 | 21 | mZ4UlJEfyq2tbF1gx22v9BzKwFs= 22 | 23 | JustUiKit.podspec 24 | 25 | qJPsV4CFBCXLzYR3vCswXUV+2BY= 26 | 27 | Modules/JustUiKit.swiftmodule/i386.swiftdoc 28 | 29 | ZTFzosRQNCvoMb+N8t9NagOEFQU= 30 | 31 | Modules/JustUiKit.swiftmodule/i386.swiftmodule 32 | 33 | GMNXqo2CjoVrE5FunhzeIn+26mI= 34 | 35 | Modules/module.modulemap 36 | 37 | 0umYKMV8GM7GL3trRseWFBq1sEY= 38 | 39 | 40 | files2 41 | 42 | .swift-version 43 | 44 | hash 45 | 46 | vcFAipHxYdq1qYk9I9s8cJUgDh0= 47 | 48 | hash2 49 | 50 | pBbqhEIfp+E1FYLaSCNbrIg4CjN+xctakjncfVeQi0s= 51 | 52 | 53 | Headers/JustUiKit-Swift.h 54 | 55 | hash 56 | 57 | q5hJZ57zzllNSYHIjOATlcfsXFs= 58 | 59 | hash2 60 | 61 | RA9hbKcF0wiIGgsvNh4wUGBbDXxOXN+m8U+JxuQxjuw= 62 | 63 | 64 | Headers/JustUiKit.h 65 | 66 | hash 67 | 68 | apLsWgBOz9JiVcsrWzwvrrqC9XU= 69 | 70 | hash2 71 | 72 | mIrKNajKGQsTZbWll9iTK8JhwIqUqMEHM+TxSQ7P/t0= 73 | 74 | 75 | JustUiKit.podspec 76 | 77 | hash 78 | 79 | qJPsV4CFBCXLzYR3vCswXUV+2BY= 80 | 81 | hash2 82 | 83 | w9ekR9hDQ4QGPEm17VN0AMV60mY0xoOjLuKIV/AjWCw= 84 | 85 | 86 | Modules/JustUiKit.swiftmodule/i386.swiftdoc 87 | 88 | hash 89 | 90 | ZTFzosRQNCvoMb+N8t9NagOEFQU= 91 | 92 | hash2 93 | 94 | GIknIJ6tMhevxSgRQl7CZV2dIAA03FGQNbyf9MXziEU= 95 | 96 | 97 | Modules/JustUiKit.swiftmodule/i386.swiftmodule 98 | 99 | hash 100 | 101 | GMNXqo2CjoVrE5FunhzeIn+26mI= 102 | 103 | hash2 104 | 105 | 0xg4fKJmND/wSMyPTmTBFI37nDPvw6qqC+ewegWoDaU= 106 | 107 | 108 | Modules/module.modulemap 109 | 110 | hash 111 | 112 | 0umYKMV8GM7GL3trRseWFBq1sEY= 113 | 114 | hash2 115 | 116 | pPWL6choMH7wLjDLG2VBJ9kSkd5LCwJOiiUTtTvoPtM= 117 | 118 | 119 | 120 | rules 121 | 122 | ^ 123 | 124 | ^.*\.lproj/ 125 | 126 | optional 127 | 128 | weight 129 | 1000 130 | 131 | ^.*\.lproj/locversion.plist$ 132 | 133 | omit 134 | 135 | weight 136 | 1100 137 | 138 | ^Base\.lproj/ 139 | 140 | weight 141 | 1010 142 | 143 | ^version.plist$ 144 | 145 | 146 | rules2 147 | 148 | .*\.dSYM($|/) 149 | 150 | weight 151 | 11 152 | 153 | ^ 154 | 155 | weight 156 | 20 157 | 158 | ^(.*/)?\.DS_Store$ 159 | 160 | omit 161 | 162 | weight 163 | 2000 164 | 165 | ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ 166 | 167 | nested 168 | 169 | weight 170 | 10 171 | 172 | ^.* 173 | 174 | ^.*\.lproj/ 175 | 176 | optional 177 | 178 | weight 179 | 1000 180 | 181 | ^.*\.lproj/locversion.plist$ 182 | 183 | omit 184 | 185 | weight 186 | 1100 187 | 188 | ^Base\.lproj/ 189 | 190 | weight 191 | 1010 192 | 193 | ^Info\.plist$ 194 | 195 | omit 196 | 197 | weight 198 | 20 199 | 200 | ^PkgInfo$ 201 | 202 | omit 203 | 204 | weight 205 | 20 206 | 207 | ^[^/]+$ 208 | 209 | nested 210 | 211 | weight 212 | 10 213 | 214 | ^embedded\.provisionprofile$ 215 | 216 | weight 217 | 20 218 | 219 | ^version\.plist$ 220 | 221 | weight 222 | 20 223 | 224 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /JustUiKit/layout/JustExtensionView.swift: -------------------------------------------------------------------------------- 1 | /// MIT License 2 | /// 3 | /// Copyright (c) 2017 JustWe 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 | 23 | 24 | import UIKit 25 | 26 | public protocol ExtensionParams { 27 | func setLayoutParams(params: LayoutParams) 28 | 29 | func getLayoutParams() -> LayoutParams 30 | } 31 | 32 | public struct Padding { 33 | var paddingLeft: CGFloat = 0 34 | var paddingRight: CGFloat = 0 35 | var paddingBottom: CGFloat = 0 36 | var paddingTop: CGFloat = 0 37 | 38 | public init() { 39 | 40 | } 41 | 42 | public init(top: CGFloat, left: CGFloat, right: CGFloat, bottom: CGFloat) { 43 | self.paddingTop = top 44 | self.paddingLeft = left 45 | self.paddingRight = right 46 | self.paddingBottom = bottom 47 | } 48 | /** 49 | ** delete when size - padding < 0 (isHidden) 50 | */ 51 | public func getMinSize(size: CGSize) -> CGSize { 52 | return CGSize(width: max(size.width - paddingLeft - paddingRight, 0), 53 | height: max(size.height - paddingTop - paddingBottom, 0)) 54 | } 55 | 56 | public func getMaxSize(size: CGSize) -> CGSize { 57 | return CGSize(width: max(size.width + paddingLeft + paddingRight, 0), 58 | height: max(size.height + paddingTop + paddingBottom, 0)) 59 | } 60 | } 61 | 62 | public struct UIViewExtension { 63 | public var padding: Padding 64 | internal var layoutParams: LayoutParams 65 | internal init(padding: Padding, params: LayoutParams) { 66 | self.padding = padding 67 | self.layoutParams = params 68 | } 69 | 70 | internal init() { 71 | self.padding = Padding() 72 | self.layoutParams = LayoutParams.generateDefaultLayoutParams() 73 | } 74 | 75 | public mutating func setLayoutParams(params: LayoutParams) { 76 | params.bindWith(view:layoutParams.bindView) 77 | self.layoutParams = params 78 | } 79 | } 80 | 81 | extension UIView: ExtensionParams { 82 | 83 | private struct Key { 84 | static var ExtensionKey = "ExtensionKey" 85 | } 86 | 87 | public var uiViewExtension: UIViewExtension { 88 | set { 89 | objc_setAssociatedObject(self, &Key.ExtensionKey, 90 | newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 91 | } 92 | 93 | get { 94 | if let el = objc_getAssociatedObject(self, &Key.ExtensionKey) as? UIViewExtension { 95 | return el 96 | } 97 | let el = UIViewExtension() 98 | objc_setAssociatedObject(self, &Key.ExtensionKey, 99 | el, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 100 | return el 101 | } 102 | } 103 | 104 | public func setLayoutParams(params: LayoutParams) { 105 | uiViewExtension.layoutParams = params 106 | } 107 | 108 | public func getLayoutParams() -> LayoutParams { 109 | return uiViewExtension.layoutParams 110 | } 111 | 112 | public func setPadding(top: CGFloat, left: CGFloat, right: CGFloat, bottom: CGFloat) { 113 | self.uiViewExtension.padding = Padding(top: top, left: left, right: right, bottom: bottom) 114 | } 115 | } 116 | 117 | extension UIView { 118 | 119 | private struct IdKey { 120 | static var ExtensionKey = "IdExtensionKey" 121 | } 122 | 123 | private static var atomicInteger = 1 124 | 125 | public class InnerInteger { 126 | let viewId: Int 127 | 128 | init(_ id: Int) { 129 | viewId = id 130 | } 131 | 132 | public static func ==(_ a: InnerInteger, _ b: InnerInteger) -> Bool { 133 | if a.viewId == b.viewId { 134 | return true 135 | } 136 | return false 137 | } 138 | } 139 | 140 | private var viewId: InnerInteger { 141 | set { 142 | objc_setAssociatedObject(self, &IdKey.ExtensionKey, 143 | newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 144 | } 145 | 146 | get { 147 | if let el = objc_getAssociatedObject(self, &IdKey.ExtensionKey) as? InnerInteger { 148 | return el 149 | } 150 | let el = InnerInteger(-1) 151 | objc_setAssociatedObject(self, &IdKey.ExtensionKey, 152 | el, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 153 | return el 154 | } 155 | } 156 | 157 | public static func generateViewId() -> Int { 158 | objc_sync_enter(atomicInteger) 159 | atomicInteger += 1 160 | objc_sync_exit(atomicInteger) 161 | return Int(atomicInteger) 162 | } 163 | 164 | public func setViewId(id: Int) { 165 | viewId = InnerInteger(id) 166 | } 167 | 168 | public func getViewId() -> InnerInteger { 169 | return viewId 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /JustUiKitSample/JustUiKitSample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // JustUiKitSample 4 | // 5 | // Created by 刘丰恺 on 2017/2/6. 6 | // Copyright (c) 2017 ___lfkdsk___. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import JustUiKit 11 | 12 | class ViewController: UIViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | // Do any additional setup after loading the view, typically from a nib. 17 | 18 | } 19 | 20 | 21 | override func didReceiveMemoryWarning() { 22 | super.didReceiveMemoryWarning() 23 | // Dispose of any resources that can be recreated. 24 | } 25 | 26 | func createSectionLabel(_ title: String) -> UILabel { 27 | let sectionLabel = UILabel() 28 | sectionLabel.text = title; 29 | sectionLabel.font = UIFont.systemFont(ofSize: 17) 30 | sectionLabel.sizeToFit() 31 | sectionLabel.backgroundColor = UIColor.brown 32 | return sectionLabel 33 | } 34 | 35 | func loadLinearLayout() { 36 | self.view = UIView(frame: UIScreen.main.bounds) 37 | view.backgroundColor = UIColor(rgb: 0xFF8C00) 38 | 39 | let lfkdsk = JustLinearLayout(frame: UIScreen.main.bounds, orientation: .Horizontal) 40 | 41 | view.addSubview(lfkdsk) 42 | 43 | let linearLayout: JustLinearLayout = lfkdsk 44 | 45 | let params: LinearLayoutParams = LinearLayoutParams( 46 | width: LayoutParams.WRAP_CONTENT, 47 | height: LayoutParams.WRAP_CONTENT) 48 | params.weight = 1 49 | // params.leftMargin = 100 50 | // params.rightMargin = 10 51 | 52 | let fuckView = createSectionLabel("FUCK") 53 | 54 | print(fuckView.frame.origin) 55 | print(fuckView.frame.size) 56 | 57 | print("Before Layout") 58 | 59 | let centerParams = LinearLayoutParams(linear: params) 60 | // centerParams.weight = 1 61 | centerParams.layoutGravity = Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL 62 | 63 | let rightParams = LinearLayoutParams(linear: params) 64 | rightParams.layoutGravity = Gravity.BOTTOM 65 | 66 | let rightAndBottomParams = LinearLayoutParams(params) 67 | // rightAndBottomParams.weight = 1 68 | rightAndBottomParams.layoutGravity = Gravity.BOTTOM | Gravity.RIGHT 69 | 70 | let rightAndTopParams = LinearLayoutParams(linear: params) 71 | // rightAndTopParams.rightMargin = 12 72 | // rightAndTopParams.weight = 1 73 | rightAndTopParams.layoutGravity = Gravity.TOP | Gravity.RIGHT 74 | 75 | linearLayout.addView(view: createSectionLabel("苟"), params: rightAndTopParams) 76 | linearLayout.addView(view: createSectionLabel("利"), params: params) 77 | linearLayout.addView(view: createSectionLabel("国"), params: params) 78 | // linearLayout.addView(view: createSectionLabel("家"), params: centerParams) 79 | // linearLayout.addView(view: createSectionLabel("生"), params: params) 80 | // linearLayout.addView(view: createSectionLabel("死"), params: rightAndBottomParams) 81 | // linearLayout.addView(view: createSectionLabel("以"), params: params) 82 | // linearLayout.addView(view: createSectionLabel("岂因祸福"), params: params) 83 | // linearLayout.addView(view: createSectionLabel("岂因祸福"), params: params) 84 | // linearLayout.addView(view: createSectionLabel("岂因祸福"), params: params) 85 | // linearLayout.addView(view: createSectionLabel("以"), params: params) 86 | 87 | let fuck2View = JustLinearLayout(orientation: .Horizontal) 88 | // fuck2View.linearExtension.padding.paddingBottom = 10 89 | let Fuck2Params = LinearLayoutParams(width: LayoutParams.MATCH_PARENT, 90 | height: LayoutParams.WRAP_CONTENT) 91 | // Fuck2Params.topMargin = 80 92 | Fuck2Params.leftMargin = 20 93 | Fuck2Params.weight = 1 94 | Fuck2Params.layoutGravity = Gravity.CENTER_VERTICAL 95 | // fuck2View.setPadding(top: 10, left: 0, right: 10, bottom: 0) 96 | lfkdsk.addView(view: fuck2View, params: Fuck2Params) 97 | 98 | let firstInnerItem = createSectionLabel("以") 99 | // rightAndTopParams.bottomMargin = 10 100 | 101 | fuck2View.addView(view: firstInnerItem, params: params) 102 | fuck2View.addView(view: createSectionLabel("岂因祸福"), params: params) 103 | fuck2View.addView(view: createSectionLabel("岂因祸福"), params: params) 104 | fuck2View.addView(view: createSectionLabel("岂因祸福"), params: params) 105 | fuck2View.addView(view: createSectionLabel("岂因祸福"), params: rightParams) 106 | fuck2View.addView(view: createSectionLabel("岂因祸福"), params: params) 107 | fuck2View.addView(view: createSectionLabel("岂因祸福"), params: centerParams) 108 | // fuck2View.linearExtension.padding.paddingRight = 80 109 | 110 | } 111 | 112 | func loadFrameLayout() { 113 | self.view = JustFrameLayout(frame: UIScreen.main.bounds) 114 | view.backgroundColor = UIColor(rgb: 0xFF8C00) 115 | 116 | let lfkdsk: JustFrameLayout = view as! JustFrameLayout 117 | // (view as! JustFrameLayout).addView(view: lfkdsk, params: FrameLayoutParams(width: 118 | // LayoutParams.WRAP_CONTENT, height: LayoutParams.WRAP_CONTENT)) 119 | 120 | let params = FrameLayoutParams(width: LayoutParams.WRAP_CONTENT, 121 | height: LayoutParams.WRAP_CONTENT) 122 | 123 | let CENTER_V = FrameLayoutParams(source: params) 124 | CENTER_V.layoutGravity = Gravity.CENTER_HORIZONTAL 125 | 126 | let CENTER_H = FrameLayoutParams(source: params) 127 | CENTER_H.layoutGravity = Gravity.CENTER_VERTICAL 128 | 129 | let bottom_left = FrameLayoutParams(source: params) 130 | bottom_left.layoutGravity = Gravity.BOTTOM | Gravity.LEFT 131 | 132 | let ver_right = FrameLayoutParams(source: params) 133 | ver_right.layoutGravity = Gravity.CENTER_VERTICAL | Gravity.RIGHT 134 | 135 | let center = FrameLayoutParams(source: params) 136 | center.layoutGravity = Gravity.CENTER 137 | 138 | lfkdsk.addView(view: createSectionLabel("FFFFFFFFFF"), params: CENTER_V) 139 | lfkdsk.addView(view: createSectionLabel("ssssssss"), params: CENTER_H) 140 | lfkdsk.addView(view: createSectionLabel("aaaaa"), params: bottom_left) 141 | lfkdsk.addView(view: createSectionLabel("vvvv"), params: ver_right) 142 | lfkdsk.addView(view: createSectionLabel("ssss"), params: center) 143 | } 144 | 145 | func loadRelativeLayout() { 146 | self.view = JustRelativeLayout(frame: UIScreen.main.bounds) 147 | view.backgroundColor = UIColor(rgb: 0xFF8C00) 148 | 149 | let lfkdsk: JustRelativeLayout = view as! JustRelativeLayout 150 | 151 | let params = RelativeLayoutParams(width: LayoutParams.WRAP_CONTENT, 152 | height: LayoutParams.WRAP_CONTENT) 153 | 154 | let view1 = createSectionLabel("FFF") 155 | 156 | 157 | } 158 | 159 | func addViewToLayout() { 160 | self.view = JustRelativeLayout(frame: UIScreen.main.bounds) 161 | view.backgroundColor = UIColor(rgb: 0xFF8C00) 162 | 163 | let lfkdsk: JustRelativeLayout = view as! JustRelativeLayout 164 | 165 | let view1 = createSectionLabel("11111111111111111111") 166 | let view2 = createSectionLabel("22222222222222222222") 167 | let view3 = createSectionLabel("33333333333333333333") 168 | 169 | let params = RelativeLayoutParams.generateDefaultParams() 170 | 171 | params.leftOf(view2) 172 | // params.alignLeftTo(left: view3) 173 | params.bellowOf(view3) 174 | params.topMargin = 10 175 | // 2 <==== 1 ====> 3 176 | 177 | // 3 178 | // | 179 | // 1 ==== 2 180 | lfkdsk.addView(view: view1, params: params) 181 | 182 | let params2 = RelativeLayoutParams(params) 183 | params2.layoutGravity = Gravity.RIGHT 184 | 185 | lfkdsk.addView(view: view2, params: params2) 186 | 187 | let params_center = RelativeLayoutParams(params) 188 | params_center.layoutGravity = Gravity.CENTER 189 | 190 | lfkdsk.addView(view: view3, params: params_center) 191 | } 192 | 193 | override func loadView() { 194 | super.loadView() 195 | 196 | // loadFrameLayout() 197 | addViewToLayout() 198 | } 199 | 200 | } 201 | -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | # JustUiKit 2 | JustUiKit是一套Android样式工具iOS UI套件。 JustUiKit包含JustLinearLayout,JustFrameLayout, JustRelativeLayout等。 它旨在使Android开发人员轻松构建iOS UI。 也为iOS开发人员提供了一种新的方式来构建UI。 3 | 4 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/lfkdsk/JustUiKit/master/LICENSE) 5 | [![](https://img.shields.io/badge/JustUiKit-v0.1.4-green.svg)](https://github.com/lfkdsk/JustUiKit) 6 | [![](https://img.shields.io/badge/support-iOS8%2B-green.svg)](https://github.com/lfkdsk/JustUiKit) 7 | [![CocoaPods](https://img.shields.io/cocoapods/v/JustUiKit.svg?style=flat-square)](https://cocoapods.org/pods/JustUiKit) 8 | 9 | ## 如何使用? 10 | 11 | * 拷贝代码 12 | 13 | ​ 拷贝Deomo中的JustUiKit代码到项目 14 | 15 | * 使用 CocoaPods 安装 16 | 17 | CocoaPods是Objective-C / Swift的依赖项管理器,它自动化和简化在项目中使用第三方库(JustUiKit)的过程。 您可以使用以下命令安装它: 18 | 19 | `$ sudo gem install cocoapods` 20 | 21 | 要使用CocoaPods将JustUiKit集成到您的Xcode项目中,请在Podfile中指定: 22 | 23 | ```ruby 24 | platform :ios, '8.0' 25 | pod 'JustUiKit', '~> 0.2.1' 26 | ``` 27 | 28 | 之后运行如下命令: 29 | 30 | `$ pod install` 31 | 32 | ## 快捷开始 33 | 34 | ### JustLinearLayout 35 | 将其子代排列在单列或单行中的布局。 行的方向可以通过方向设置。 您还可以指定gravity,它通过设置重力指定所有子元素的对齐方式,或通过设置LinearLayoutParams的weight成员来指定特定子项增长以填充布局中的任何剩余空间。 默认方向为水平。 36 | 37 | #### Gravity & Margin & Orientation 38 | ![Vertical](art/vertical_layout.png) 39 | 40 | 您可以设置重力以使视图布局在parentView的指定空间。 还设置Margin将在视图的四个方向保留空格。 在默认情况下,LinearLayout布局子项在水平方向,并且可以将其更改为Veritcal。 41 | 42 | ``` swift 43 | let parentView = JustLinearLayout(frame: UIScreen.main.bounds, orientation: .Vertical) 44 | let params = LinearLayoutParams( 45 | width: LayoutParams.WRAP_CONTENT, 46 | height: LayoutParams.WRAP_CONTENT) 47 | let centerParams = LinearLayoutParams(params) 48 | centerParams.layoutGravity = Gravity.Horizontal.getValue() 49 | 50 | let marginParams = LinearLayoutParams(params) 51 | marginParams.topMargin = 10 52 | 53 | parentView.addView(createView(), centerParams) 54 | parentView.addView(createView(), marginParams) 55 | ``` 56 | 57 | #### Padding & weight 58 | ![Horizontal](art/horizontal_layout.png) 59 | 60 | 使用Padding在四个方向,您可以保留视图的内部空间。 “权重”描述了子视图的位置。 默认为Gravity.TOP | Gravity.LEFT。 如果此布局具有VERTICAL方向,则控制在存在额外垂直空间时放置所有子视图的位置。 如果此布局具有HORIZONTAL方向,则控制子项的对齐方式。 61 | 62 | ``` swift 63 | let parentView = JustLinearLayout(frame: UIScreen.main.bounds, orientation: .Vertical) 64 | let params = LinearLayoutParams( 65 | width: LayoutParams.WRAP_CONTENT, 66 | height: LayoutParams.WRAP_CONTENT) 67 | params.weight = 1 68 | let paddingParams = LinearLayoutParams(params) 69 | paddingParams.paddingTop = 10 70 | parentView.addView(createView(), params) 71 | parentView.addView(createView(), paddingParams) 72 | ``` 73 | 74 | ### JustFrameLayout 75 | 76 | FrameLayout设计用于屏蔽屏幕上的一个区域以显示单个项目。 77 | 78 | 通常,FrameLayout应该用于保存单个子视图,因为可能难以以可扩展到不同屏幕尺寸的方式组织子视图,而不会使子节点相互重叠。 79 | 80 | 然而,您可以添加多个孩子到一个FrameLayout,并通过分配重力给每个孩子,使用layoutGravity控制他们在FrameLayout中的位置。 81 | 82 | #### Overlap on left 83 | ![Frame1](art/framelayout1.png) 84 | 85 | 如果不使用任何边距或填充来更改视图的位置。 所有的视图将添加像堆栈和重叠在屏幕的左边|顶部。 86 | 87 | Example: 88 | 89 | ``` swift 90 | let parentView: JustFrameLayout = JustFrameLayout(width:MATCH_PARENT, height:MATCH_PARENT) 91 | let params: FrameLayoutParams = JustFrameLayoutParams(width: WRAP_CONTENT, height: WRAP_CONTENT) 92 | parentView.addView(createView(rgb:0xE4E1D8), params) 93 | parentView.addView(createView(rgb:0x89A49D), params) 94 | parentView.addView(createView(rgb:0x877B6B), params) 95 | ``` 96 | 97 | #### Layout With Gravity 98 | ![Frame2](art/framelayout2.png) 99 | 100 | 您可以使用水平和垂直重力。 此外,您可以使用像left | bottom,center_horizontal | center_vertical来同时使用它们。 101 | 102 | Example: 103 | 104 | ``` swift 105 | let parentView: FrameLayout = FrameLayout(width:MATCH_PARENT, height:MATCH_PARENT) 106 | let params: FrameLayoutParams = FrameLayoutParams(width: WRAP_CONTENT, height: WRAP_CONTENT) 107 | 108 | let b_l = FrameLayoutParams(params) 109 | b_l.layoutGravity = Gravity.BOTTOM | Gravity.TOP 110 | let c_r = FrameLayoutParams(params) 111 | c_r.layoutGravity = Gravity.CENTER_HORIZONTAL | Gravity.RIGHT 112 | parentView.addView(createView(rgb:0xE4E1D8), b_l) 113 | parentView.addView(createView(rgb:0x89A49D), c_r) 114 | parentView.addView(createView(rgb:0x877B6B), params) 115 | ``` 116 | 117 | ### JustRelativeLayout 118 | 一个布局,其中子节点的位置可以相对于彼此或相对于父节点来描述。 119 | 120 | 请注意,您不能在RelativeLayout的大小和其子项的位置之间具有循环依赖关系。 例如,您不能具有高度设置为WRAP_CONTENT的RelativeLayout和设置为ALIGN_PARENT_BOTTOM的子级。 121 | 122 | ![relative](art/relativelayout1.png) 123 | 124 | 在RelativeLayout中,您可以使用所有边距,填充和重力。 此外,您可以使用一些对齐功能。 例如,如果视图是alignLeftTo,则其左边缘将被设置为等于锚视图。 您可以使用一组函数,如leftOf,rightOf,bottomOf将当前视图设置到锚视图的左侧。 125 | 126 | ``` swift 127 | // view1 view2 view3 view4 view5 128 | let params = RelativeLayoutParams.generateDefaultParams() 129 | let params1 = RelativeLayoutParams(params) 130 | params1.centerInHorizontal() 131 | let params2 = RelativeLayoutParams(params) 132 | params2.bottomTo(view4) 133 | params2.topMargin = xxx 134 | let params3 = RelativeLayoutParams(params) 135 | param3.alignRightTo(view1) 136 | let params4 = RelativeLayoutParams(params) 137 | params4.centerInParent() 138 | let params5 = RelativeLayoutParams(params) 139 | params5.alignParentBottom() 140 | // add view to parent 141 | ``` 142 | 143 | #### RelativeLayout中的约束 144 | 145 | | Constants | Description | 146 | | ------------------- | -------------------------------------- | 147 | | ABOVE | 将孩子的底部边缘与另一个孩子的顶部边缘对齐的规则。 | 148 | | ALIGN_BASELINE | 将子项的基准与另一个子项的基准对齐的规则。 | 149 | | ALIGN_BOTTOM | 将儿童的底部边缘与另一个儿童的底部边缘对齐的规则。 | 150 | | ALIGN_LEFT | 将儿童的左边缘与另一个儿童的左边缘对齐的规则。 | 151 | | ALIGN_PARENT_BOTTOM | 将子元素的底边与其RelativeLayout父元素的底边对齐的规则。 | 152 | | ALIGN_PARENT_LEFT | 将子对象的左边缘与其RelativeLayout父对象的左边缘对齐的规则。 | 153 | | ALIGN_PARENT_RIGHT | 将子对象的右边缘与其RelativeLayout父对象的右边缘对齐的规则。 | 154 | | ALIGN_PARENT_TOP | 将子对象的顶边与其RelativeLayout父对象的顶边对齐的规则。 | 155 | | ALIGN_RIGHT | 将孩子的右边缘与另一个孩子的右边缘对齐的规则。 | 156 | | ALIGN_TOP | 将儿童的顶部边缘与另一个儿童的顶部边缘对齐的规则。 | 157 | | BELOW | 将儿童的顶部边缘与另一个儿童的底部边缘对齐的规则。 | 158 | | CENTER_HORIZONTAL | 将子元素相对于其RelativeLayout父元素的边界进行水平居中的规则。 | 159 | | CENTER_IN_PARENT | 使子对象相对于其RelativeLayout父对象的边界居中的规则。 | 160 | | CENTER_VERTICAL | 将子元素相对于其RelativeLayout父元素的边界垂直居中的规则。 | 161 | | END_OF | 将孩子的起始边缘与另一个孩子的边缘对齐的规则。 | 162 | | LEFT_OF | 将儿童的右边缘与另一个儿童的左边缘对齐的规则。 | 163 | | RIGHT_OF | 将儿童的左边缘与另一个儿童的右边缘对齐的规则。 | 164 | 165 | ## Gravity 166 | 167 | 必须是以下常量值中的一个或多个(由“|”分隔)。 168 | 169 | | Constant | Value | Description | 170 | | :---------------- | ----- | ---------------------------- | 171 | | 顶部 | 0x30 | 将对象推送到其容器的顶部,而不更改其大小。 | 172 | | 底部 | 0x50 | 将对象推送到其容器的底部,而不更改其大小。 | 173 | | 右 | 0x05 | 将对象推送到其容器的右侧,而不更改其大小。 | 174 | | center_vertical | 0x10 | 将对象放置在其容器的垂直中心,而不是更改其大小。 | 175 | | center_horizontal | 0x01 | 将对象放置在其容器的水平中心,而不改变其大小。 | 176 | | 中心 | 0x11 | 将对象放置在其容器的垂直和水平轴的中心,而不改变其大小。 | 177 | 178 | 179 | ## Feedback 180 | 只要发生问题,请发送您的反馈。 你可以与我联系: 181 | * Email: lfk_dsk@hotmail.com 182 | * Weibo: [@亦狂亦侠_亦温文](http://www.weibo.com/u/2443510260) 183 | * Blog: [刘丰恺](https://lfkdsk.github.io) 184 | 185 | ## License 186 | 187 | ``` 188 | 189 | MIT License 190 | 191 | Copyright (c) 2017 JustWe 192 | 193 | Permission is hereby granted, free of charge, to any person obtaining a copy 194 | of this software and associated documentation files (the "Software"), to deal 195 | in the Software without restriction, including without limitation the rights 196 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 197 | copies of the Software, and to permit persons to whom the Software is 198 | furnished to do so, subject to the following conditions: 199 | 200 | The above copyright notice and this permission notice shall be included in all 201 | copies or substantial portions of the Software. 202 | 203 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 204 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 205 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 206 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 207 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 208 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 209 | SOFTWARE. 210 | ``` 211 | -------------------------------------------------------------------------------- /JustUiKit/widget/JustFrameLayout.swift: -------------------------------------------------------------------------------- 1 | /// MIT License 2 | /// 3 | /// Copyright (c) 2017 JustWe 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 | 23 | import UIKit 24 | 25 | /// FrameLayout Params 26 | /// Params For FrameLayout 27 | /// This params just has layoutGravity 28 | 29 | public class FrameLayoutParams: MarginLayoutParams { 30 | public var layoutGravity: Gravity = Gravity.NO_GRAVITY 31 | 32 | public init(width: CGFloat, height: CGFloat, layoutGravity: Gravity) { 33 | super.init(width: width, height: height) 34 | self.layoutGravity = layoutGravity 35 | } 36 | 37 | public override init(width: CGFloat, height: CGFloat) { 38 | super.init(width: width, height: height) 39 | } 40 | 41 | public init(source: FrameLayoutParams) { 42 | super.init(source: source) 43 | self.layoutGravity = source.layoutGravity 44 | } 45 | 46 | /// init 47 | /// - LayoutParams params base generate method 48 | override init(_ source: LayoutParams) { 49 | super.init(source) 50 | } 51 | 52 | public static func generateDefaultParams() -> FrameLayoutParams { 53 | return FrameLayoutParams(width: LayoutParams.WRAP_CONTENT, height: LayoutParams.WRAP_CONTENT) 54 | } 55 | } 56 | 57 | open class JustFrameLayout: JustViewGroup { 58 | 59 | private var childViewSizes: [CGSize] = [] 60 | 61 | override open func sizeThatFits(_ size: CGSize) -> CGSize { 62 | let childSizes: [CGSize] 63 | var childLayoutSize = CGSize.zero 64 | var padding: Padding 65 | var index: Int = 0 66 | 67 | childSizes = measureChildren(size) 68 | 69 | for size in childSizes { 70 | padding = subviews[index].uiViewExtension.padding 71 | childLayoutSize.width = max(childLayoutSize.width, size.width + padding.paddingLeft + padding.paddingRight) 72 | childLayoutSize.height = max(childLayoutSize.height, size.height + padding.paddingTop + padding.paddingBottom) 73 | index += 1 74 | } 75 | return uiViewExtension.padding.getMaxSize(size: childLayoutSize) 76 | } 77 | 78 | 79 | override public func onLayout(_ changed: Bool, _ top: CGFloat, _ left: CGFloat, _ right: CGFloat, _ bottom: CGFloat) { 80 | super.onLayout(changed, top, left, right, bottom) 81 | 82 | let padding = uiViewExtension.padding 83 | 84 | let pTop = top + padding.paddingTop, 85 | pLeft = left + padding.paddingLeft, 86 | pRight = right - padding.paddingRight, 87 | pBottom = bottom - padding.paddingBottom 88 | 89 | layoutChildren(changed, pTop, pLeft, pRight, pBottom) 90 | } 91 | 92 | override public func onMeasure(_ size: CGSize) { 93 | super.onMeasure(size) 94 | childViewSizes = measureChildren(size) 95 | } 96 | 97 | private func measureChildren(_ size: CGSize) -> [CGSize] { 98 | let selfSize = uiViewExtension.padding.getMinSize(size: size) 99 | 100 | var childSizes = [CGSize](repeating: CGSize(width: -1, height: -1), count: subviews.count) 101 | let height = selfSize.height 102 | let width = selfSize.width 103 | let maxHeightSize: CGFloat = height 104 | let maxWidthSize: CGFloat = width 105 | 106 | for (index, child) in subviews.enumerated() { 107 | if child.isHidden { 108 | continue 109 | } 110 | 111 | let params: FrameLayoutParams = child.uiViewExtension.layoutParams as! FrameLayoutParams 112 | 113 | var childHeight: CGFloat = 0 114 | var childSize: CGSize = CGSize() 115 | 116 | switch params.height { 117 | case LayoutParams.MATCH_PARENT: 118 | childHeight = maxHeightSize 119 | case LayoutParams.WRAP_CONTENT: 120 | childSize = child.sizeThatFits( 121 | CGSize(width: maxWidthSize, 122 | height: maxHeightSize)) 123 | childHeight = childSize.height 124 | default: 125 | childHeight = params.height 126 | } 127 | 128 | childHeight = min(childHeight, maxHeightSize) 129 | childSizes[index].height = childHeight 130 | 131 | if childSize.height == childHeight { 132 | childSizes[index].width = childSize.width 133 | } 134 | 135 | var childWidth: CGFloat = 0 136 | childSize = CGSize.zero 137 | 138 | switch (params.width) { 139 | case LayoutParams.MATCH_PARENT: 140 | childWidth = maxWidthSize 141 | case LayoutParams.WRAP_CONTENT: 142 | childSize = child.sizeThatFits( 143 | CGSize(width: maxWidthSize, 144 | height: maxHeightSize)) 145 | 146 | childWidth = childSize.width 147 | default: 148 | childWidth = params.width 149 | } 150 | 151 | childWidth = min(childWidth, maxWidthSize) 152 | 153 | childSizes[index].width = childWidth 154 | } 155 | return childSizes 156 | } 157 | 158 | private func layoutChildren(_ changed: Bool, 159 | _ top: CGFloat, 160 | _ left: CGFloat, 161 | _ right: CGFloat, 162 | _ bottom: CGFloat) { 163 | 164 | let height = bottom - top 165 | 166 | for (index, size) in childViewSizes.enumerated() { 167 | let child = subviews[index] 168 | 169 | if child.isHidden { 170 | continue 171 | } 172 | 173 | let childParams: FrameLayoutParams = child.uiViewExtension.layoutParams as! FrameLayoutParams 174 | let childWidth = size.width 175 | let childHeight = size.height 176 | var childTop: CGFloat = 0 177 | var childLeft: CGFloat = 0 178 | 179 | let verticalGravity = Gravity.getVerticalGravity(gravity: childParams.layoutGravity) 180 | let horizontalGravity = Gravity.getHorizontalGravity(gravity: childParams.layoutGravity) 181 | 182 | switch verticalGravity { 183 | case Gravity.CENTER_VERTICAL.getValue(): 184 | childTop = (height - childHeight) / 2 185 | childTop += CGFloat(childParams.topMargin - childParams.bottomMargin) 186 | case Gravity.BOTTOM.getValue(): 187 | childTop = height - childHeight 188 | default: 189 | childTop = top + CGFloat(childParams.topMargin) 190 | } 191 | 192 | switch horizontalGravity { 193 | case Gravity.CENTER_HORIZONTAL.getValue(): 194 | childLeft = (right - left - childWidth) / 2 + left 195 | childLeft += (CGFloat(childParams.leftMargin - childParams.rightMargin)) 196 | case Gravity.RIGHT.getValue(): 197 | childLeft = right - left - childWidth - CGFloat(childParams.rightMargin); 198 | default: 199 | childLeft = left + CGFloat(childParams.leftMargin) 200 | } 201 | 202 | child.frame = CGRect( 203 | x: childLeft, 204 | y: childTop, 205 | width: childWidth, 206 | height: childHeight) 207 | child.layoutSubviews() 208 | } 209 | } 210 | 211 | public func addView(view: UIView, params: FrameLayoutParams = FrameLayoutParams.generateDefaultParams()) { 212 | view.uiViewExtension.layoutParams = params 213 | 214 | if view.superview != nil { 215 | return 216 | } 217 | 218 | if view is JustViewGroup { 219 | (view as! JustViewGroup).setParent(viewGroup: self) 220 | } 221 | 222 | addSubview(view) 223 | params.bindWith(view: view) 224 | } 225 | 226 | override public func addView(view: UIView) { 227 | super.addView(view: view) 228 | self.addView(view: view, params: FrameLayoutParams.generateDefaultParams()) 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JustUiKit 2 | iOS UI Kit With Android-Style Tools. JustUiKit contains `JustLinearLayout`, `JustFrameLayout` and so on. It is designed to make Android developers build iOS UI easily. Also for iOS developers, it provides a new way to build UI. 3 | 4 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/lfkdsk/JustUiKit/master/LICENSE) 5 | [![](https://img.shields.io/badge/JustUiKit-v0.1.4-green.svg)](https://github.com/lfkdsk/JustUiKit) 6 | [![](https://img.shields.io/badge/support-iOS8%2B-green.svg)](https://github.com/lfkdsk/JustUiKit) 7 | [![CocoaPods](https://img.shields.io/cocoapods/v/JustUiKit.svg?style=flat-square)](https://cocoapods.org/pods/JustUiKit) 8 | 9 | [中文请参阅](README_ZH.md) 10 | 11 | ## How To Install? 12 | 13 | * Copy to your project 14 | 15 | ​ Copy JustUiKit folder from the demo project to your project 16 | 17 | * Installation with CocoaPods 18 | 19 | CocoaPods is a dependency manager for Objective-C/Swift, which automates and simplifies the process of using 3rd-party libraries like JustUiKit in your projects. You can install it with the following command: 20 | 21 | `$ sudo gem install cocoapods` 22 | 23 | To integrate JustUiKit into your Xcode project using CocoaPods, specify it in your Podfile: 24 | 25 | ``` ruby 26 | platform :ios, '8.0' 27 | pod 'JustUiKit', '~> 0.2.1' 28 | ``` 29 | 30 | ​Then, run the following command: 31 | 32 | `$ pod install` 33 | 34 | ## Quick Start 35 | 36 | ### JustLinearLayout 37 | A Layout that arranges its children in a single column or a single row. The direction of the row can be set by `orientation`. You can also specify gravity, which specifies the alignment of all the child elements by set `gravity` or specify that specific children grow to fill up any remaining space in the layout by setting the weight member of `LinearLayoutParams`. The default orientation is horizontal. 38 | 39 | #### Gravity & Margin & Orientation 40 | ![Vertical](art/vertical_layout.png) 41 | 42 | You can set `Gravity` to make Views be layouted at a specify space of parentView. Also set `Margin` will remain spaces in the four directions of the View. In default, LinearLayout layout children in `Horizontal` direction, and you can change it to `Veritcal`. 43 | 44 | Example: 45 | 46 | ``` swift 47 | let parentView = JustLinearLayout(frame: UIScreen.main.bounds, orientation: .Vertical) 48 | let params = LinearLayoutParams( 49 | width: LayoutParams.WRAP_CONTENT, 50 | height: LayoutParams.WRAP_CONTENT) 51 | let centerParams = LinearLayoutParams(params) 52 | centerParams.layoutGravity = Gravity.Horizontal.getValue() 53 | 54 | let marginParams = LinearLayoutParams(params) 55 | marginParams.topMargin = 10 56 | 57 | parentView.addView(createView(), centerParams) 58 | parentView.addView(createView(), marginParams) 59 | ``` 60 | 61 | #### Padding & weight 62 | ![Horizontal](art/horizontal_layout.png) 63 | 64 | With the `Padding` in four directions, you can remain inner space of the view. And `Weight` describes how the child views are positioned. Defaults to Gravity.TOP | Gravity.LEFT. If this layout has a VERTICAL orientation, this controls where all the child views are placed if there is extra vertical space. If this layout has a HORIZONTAL orientation, this controls the alignment of the children. 65 | 66 | See [Gravity](##Gravity) to get more details. 67 | 68 | Example: 69 | 70 | ``` swift 71 | let parentView = JustLinearLayout(frame: UIScreen.main.bounds, orientation: .Vertical) 72 | let params = LinearLayoutParams( 73 | width: LayoutParams.WRAP_CONTENT, 74 | height: LayoutParams.WRAP_CONTENT) 75 | params.weight = 1 76 | let paddingParams = LinearLayoutParams(params) 77 | paddingParams.paddingTop = 10 78 | parentView.addView(createView(), params) 79 | parentView.addView(createView(), paddingParams) 80 | ``` 81 | 82 | ### JustFrameLayout 83 | 84 | FrameLayout is designed to block out an area on the screen to display a single item. 85 | Generally, FrameLayout should be used to hold a single child view, because it can be difficult to organize child views in a way that's scalable to different screen sizes without the children overlapping each other. 86 | You can, however, add multiple children to a FrameLayout and control their position within the FrameLayout by assigning gravity to each child, using the layoutGravity. 87 | 88 | #### Overlap on left 89 | ![Frame1](art/framelayout1.png) 90 | 91 | if don't use any `Margin` or `Padding` to change view's position. All the Views will be add like stack and overlap on the `left|top` of the screen. 92 | 93 | Example: 94 | 95 | ``` swift 96 | let parentView: JustFrameLayout = JustFrameLayout(width:MATCH_PARENT, height:MATCH_PARENT) 97 | let params: FrameLayoutParams = JustFrameLayoutParams(width: WRAP_CONTENT, height: WRAP_CONTENT) 98 | parentView.addView(createView(rgb:0xE4E1D8), params) 99 | parentView.addView(createView(rgb:0x89A49D), params) 100 | parentView.addView(createView(rgb:0x877B6B), params) 101 | ``` 102 | 103 | #### Layout With Gravity 104 | ![Frame2](art/framelayout2.png) 105 | 106 | You can use horizontal and vertical gravity. Also You can use like `left|bottom`, `center_horizontal|center_vertical` to use them at the same time. 107 | 108 | See [Gravity](##Gravity) to get more details. 109 | 110 | Example: 111 | 112 | ``` swift 113 | let parentView: FrameLayout = FrameLayout(width:MATCH_PARENT, height:MATCH_PARENT) 114 | let params: FrameLayoutParams = FrameLayoutParams(width: WRAP_CONTENT, height: WRAP_CONTENT) 115 | 116 | let b_l = FrameLayoutParams(params) 117 | b_l.layoutGravity = Gravity.BOTTOM | Gravity.TOP 118 | let c_r = FrameLayoutParams(params) 119 | c_r.layoutGravity = Gravity.CENTER_HORIZONTAL | Gravity.RIGHT 120 | parentView.addView(createView(rgb:0xE4E1D8), b_l) 121 | parentView.addView(createView(rgb:0x89A49D), c_r) 122 | parentView.addView(createView(rgb:0x877B6B), params) 123 | ``` 124 | 125 | ### JustRelativeLayout 126 | A Layout where the positions of the children can be described in relation to each other or to the parent. 127 | 128 | Note that you cannot have a circular dependency between the size of the RelativeLayout and the position of its children. For example, you cannot have a RelativeLayout whose height is set to WRAP_CONTENT and a child set to ALIGN_PARENT_BOTTOM. 129 | 130 | ![relative](art/relativelayout1.png) 131 | 132 | In RelativeLayout, you can use all the `Margin`,`Padding` and `Gravity`. Also You can use some `Align` functions. Such as, if a view is `alignLeftTo`, its left edge will be set equal to the anchor view. You can use a set of functions like `leftOf`, `rightOf`, `bottomOf` set the current view to the left of anchor view. 133 | 134 | ``` swift 135 | // view1 view2 view3 view4 view5 136 | let params = RelativeLayoutParams.generateDefaultParams() 137 | let params1 = RelativeLayoutParams(params) 138 | params1.centerInHorizontal() 139 | let params2 = RelativeLayoutParams(params) 140 | params2.bottomTo(view4) 141 | params2.topMargin = xxx 142 | let params3 = RelativeLayoutParams(params) 143 | param3.alignRightTo(view1) 144 | let params4 = RelativeLayoutParams(params) 145 | params4.centerInParent() 146 | let params5 = RelativeLayoutParams(params) 147 | params5.alignParentBottom() 148 | // add view to parent 149 | ``` 150 | 151 | #### Rule in RelativeLayout 152 | 153 | | Constants | Description | 154 | | ------------------- | ---------------------------------------- | 155 | | ABOVE | Rule that aligns a child's bottom edge with another child's top edge. | 156 | | ALIGN_BASELINE | Rule that aligns a child's baseline with another child's baseline. | 157 | | ALIGN_BOTTOM | Rule that aligns a child's bottom edge with another child's bottom edge. | 158 | | ALIGN_LEFT | Rule that aligns a child's left edge with another child's left edge. | 159 | | ALIGN_PARENT_BOTTOM | Rule that aligns the child's bottom edge with its RelativeLayout parent's bottom edge. | 160 | | ALIGN_PARENT_LEFT | Rule that aligns the child's left edge with its RelativeLayout parent's left edge. | 161 | | ALIGN_PARENT_RIGHT | Rule that aligns the child's right edge with its RelativeLayout parent's right edge. | 162 | | ALIGN_PARENT_TOP | Rule that aligns the child's top edge with its RelativeLayout parent's top edge. | 163 | | ALIGN_RIGHT | Rule that aligns a child's right edge with another child's right edge. | 164 | | ALIGN_TOP | Rule that aligns a child's top edge with another child's top edge. | 165 | | BELOW | Rule that aligns a child's top edge with another child's bottom edge. | 166 | | CENTER_HORIZONTAL | Rule that centers the child horizontally with respect to the bounds of its RelativeLayout parent. | 167 | | CENTER_IN_PARENT | Rule that centers the child with respect to the bounds of its RelativeLayout parent. | 168 | | CENTER_VERTICAL | Rule that centers the child vertically with respect to the bounds of its RelativeLayout parent. | 169 | | END_OF | Rule that aligns a child's start edge with another child's end edge. | 170 | | LEFT_OF | Rule that aligns a child's right edge with another child's left edge. | 171 | | RIGHT_OF | Rule that aligns a child's left edge with another child's right edge. | 172 | 173 | ## Gravity 174 | 175 | Must be one or more (separated by '|') of the following constant values. 176 | 177 | | Constant | Value | Description | 178 | | :---------------- | ----- | ---------------------------------------- | 179 | | top | 0x30 | Push object to the top of its container, not changing its size. | 180 | | bottom | 0x50 | Push object to the bottom of its container, not changing its size. | 181 | | right | 0x05 | Push object to the right of its container, not changing its size. | 182 | | center_vertical | 0x10 | Place object in the vertical center of its container, not changing its size. | 183 | | center_horizontal | 0x01 | Place object in the horizontal center of its container, not changing its size. | 184 | | center | 0x11 | Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. | 185 | 186 | ## Feedback 187 | 188 | Please send your feedback as long as there occurs any inconvenience or problem. You can contact me with: 189 | * Email: lfk_dsk@hotmail.com 190 | * Weibo: [@亦狂亦侠_亦温文](http://www.weibo.com/u/2443510260) 191 | * Blog: [刘丰恺](https://lfkdsk.github.io) 192 | 193 | ## License 194 | 195 | ``` 196 | 197 | MIT License 198 | 199 | Copyright (c) 2017 JustWe 200 | 201 | Permission is hereby granted, free of charge, to any person obtaining a copy 202 | of this software and associated documentation files (the "Software"), to deal 203 | in the Software without restriction, including without limitation the rights 204 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 205 | copies of the Software, and to permit persons to whom the Software is 206 | furnished to do so, subject to the following conditions: 207 | 208 | The above copyright notice and this permission notice shall be included in all 209 | copies or substantial portions of the Software. 210 | 211 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 212 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 213 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 214 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 215 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 216 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 217 | SOFTWARE. 218 | 219 | ``` 220 | -------------------------------------------------------------------------------- /JustUiKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1F4A50AB70CA7FAA8452071D /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 1F4A536A1248643E93F8A2CD /* Info.plist */; }; 11 | 1F4A50DF28D4F4DB7E48B1F5 /* JustFrameLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F4A5242F547BDDE9E85737F /* JustFrameLayout.swift */; }; 12 | 1F4A52920CBEDAC4E8EBB468 /* JustExtensionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F4A53BFD8C6BC0B089C3A33 /* JustExtensionView.swift */; }; 13 | 1F4A5478A2C2D99BD50069AC /* JustLinearLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F4A5551A35FC077DE6B5B2F /* JustLinearLayout.swift */; }; 14 | 1F4A56BE01BB7181D793C388 /* ReleasePool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F4A5D2DDC60FD033F813BE6 /* ReleasePool.swift */; }; 15 | 1F4A571634F8208E3BE1671F /* JustUiKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F4A505755AC835F3762D3A3 /* JustUiKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | 1F4A57D0DE7EA6295AB293D2 /* JustViewParent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F4A5456678972DE25244C98 /* JustViewParent.swift */; }; 17 | 1F4A57E10237CAEE95A4650E /* Orientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F4A5F9AF4A2DC76D4825118 /* Orientation.swift */; }; 18 | 1F4A5944BBC556272F2CC3A8 /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F4A505849AD0FC368329099 /* ColorExtension.swift */; }; 19 | 1F4A5989D9FC754C62434510 /* JustViewManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F4A51215B79447FBF05FD4E /* JustViewManager.swift */; }; 20 | 1F4A59B149573A55F70F2B95 /* JustLayoutParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F4A5AD76626AC9CFB1358EF /* JustLayoutParams.swift */; }; 21 | 1F4A59E2B441CBC469D1081D /* JustViewGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F4A5511BC326869D730F08C /* JustViewGroup.swift */; }; 22 | 1F4A5BDA99E2B314D8F00C94 /* JustUiKit.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 1F4A5C468AA857CCB949BFEC /* JustUiKit.podspec */; }; 23 | 1F4A5C1CD4E1B9684E62A2A1 /* .swift-version in Resources */ = {isa = PBXBuildFile; fileRef = 1F4A59ACE48077F39440C6CC /* .swift-version */; }; 24 | 1F4A5CC393D38237C20F73B3 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F4A538A84DAF817FE7AABA2 /* Queue.swift */; }; 25 | 1F4A5D248CDEBE9F7C9D87B3 /* Gravity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F4A5A9ACB27277D696F9F49 /* Gravity.swift */; }; 26 | 529896541E374C1B00CA01B9 /* JustRelativeLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 529896531E374C1B00CA01B9 /* JustRelativeLayout.swift */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | 1F4A520233E995710022CAB9 /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 1F4A5EE6514429FA6E1E68F6 /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 1F4A5E9B5B37635B5E156071; 35 | remoteInfo = JustUiKit; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 1F4A505755AC835F3762D3A3 /* JustUiKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JustUiKit.h; sourceTree = ""; }; 41 | 1F4A505849AD0FC368329099 /* ColorExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorExtension.swift; sourceTree = ""; }; 42 | 1F4A51215B79447FBF05FD4E /* JustViewManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JustViewManager.swift; sourceTree = ""; }; 43 | 1F4A5242F547BDDE9E85737F /* JustFrameLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JustFrameLayout.swift; sourceTree = ""; }; 44 | 1F4A536A1248643E93F8A2CD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.info; path = Info.plist; sourceTree = ""; }; 45 | 1F4A538A84DAF817FE7AABA2 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; 46 | 1F4A53BFD8C6BC0B089C3A33 /* JustExtensionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JustExtensionView.swift; sourceTree = ""; }; 47 | 1F4A5456678972DE25244C98 /* JustViewParent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JustViewParent.swift; sourceTree = ""; }; 48 | 1F4A5511BC326869D730F08C /* JustViewGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JustViewGroup.swift; sourceTree = ""; }; 49 | 1F4A5551A35FC077DE6B5B2F /* JustLinearLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JustLinearLayout.swift; sourceTree = ""; }; 50 | 1F4A59ACE48077F39440C6CC /* .swift-version */ = {isa = PBXFileReference; lastKnownFileType = "file.swift-version"; path = ".swift-version"; sourceTree = ""; }; 51 | 1F4A5A9ACB27277D696F9F49 /* Gravity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Gravity.swift; sourceTree = ""; }; 52 | 1F4A5AD76626AC9CFB1358EF /* JustLayoutParams.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JustLayoutParams.swift; sourceTree = ""; }; 53 | 1F4A5C468AA857CCB949BFEC /* JustUiKit.podspec */ = {isa = PBXFileReference; lastKnownFileType = file.podspec; path = JustUiKit.podspec; sourceTree = ""; }; 54 | 1F4A5D2DDC60FD033F813BE6 /* ReleasePool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReleasePool.swift; sourceTree = ""; }; 55 | 1F4A5F9AF4A2DC76D4825118 /* Orientation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Orientation.swift; sourceTree = ""; }; 56 | 529896521E374C0E00CA01B9 /* JustUiKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JustUiKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 529896531E374C1B00CA01B9 /* JustRelativeLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JustRelativeLayout.swift; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 1F4A5276196EA8B81244EF6D /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | 1F4A551154CCEE13D44F75EF /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | 1F4A52985AAE5EBA23FEBDE2 /* utils */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 1F4A5A9ACB27277D696F9F49 /* Gravity.swift */, 82 | 1F4A5F9AF4A2DC76D4825118 /* Orientation.swift */, 83 | 1F4A505849AD0FC368329099 /* ColorExtension.swift */, 84 | 1F4A538A84DAF817FE7AABA2 /* Queue.swift */, 85 | 1F4A5D2DDC60FD033F813BE6 /* ReleasePool.swift */, 86 | ); 87 | path = utils; 88 | sourceTree = ""; 89 | }; 90 | 1F4A53E4C54A6588A68324D1 /* layout */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 1F4A5511BC326869D730F08C /* JustViewGroup.swift */, 94 | 1F4A5456678972DE25244C98 /* JustViewParent.swift */, 95 | 1F4A51215B79447FBF05FD4E /* JustViewManager.swift */, 96 | 1F4A53BFD8C6BC0B089C3A33 /* JustExtensionView.swift */, 97 | ); 98 | path = layout; 99 | sourceTree = ""; 100 | }; 101 | 1F4A54665BF1962B972E84B4 = { 102 | isa = PBXGroup; 103 | children = ( 104 | 1F4A54AB7F9F57E5B9752BB4 /* JustUiKit */, 105 | 1F4A59ACE48077F39440C6CC /* .swift-version */, 106 | 1F4A5C468AA857CCB949BFEC /* JustUiKit.podspec */, 107 | 5241B5D31E2F906A00F96976 /* Frameworks */, 108 | 529896521E374C0E00CA01B9 /* JustUiKitTests.xctest */, 109 | ); 110 | sourceTree = ""; 111 | }; 112 | 1F4A54AB7F9F57E5B9752BB4 /* JustUiKit */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 1F4A536A1248643E93F8A2CD /* Info.plist */, 116 | 1F4A505755AC835F3762D3A3 /* JustUiKit.h */, 117 | 1F4A52985AAE5EBA23FEBDE2 /* utils */, 118 | 1F4A5CA795838BC2E7696DD9 /* widget */, 119 | 1F4A53E4C54A6588A68324D1 /* layout */, 120 | 1F4A5CD8DDD6C55800B38D5A /* params */, 121 | ); 122 | path = JustUiKit; 123 | sourceTree = ""; 124 | }; 125 | 1F4A5CA795838BC2E7696DD9 /* widget */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 529896531E374C1B00CA01B9 /* JustRelativeLayout.swift */, 129 | 1F4A5551A35FC077DE6B5B2F /* JustLinearLayout.swift */, 130 | 1F4A5242F547BDDE9E85737F /* JustFrameLayout.swift */, 131 | ); 132 | path = widget; 133 | sourceTree = ""; 134 | }; 135 | 1F4A5CD8DDD6C55800B38D5A /* params */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 1F4A5AD76626AC9CFB1358EF /* JustLayoutParams.swift */, 139 | ); 140 | path = params; 141 | sourceTree = ""; 142 | }; 143 | 5241B5D31E2F906A00F96976 /* Frameworks */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | ); 147 | name = Frameworks; 148 | sourceTree = ""; 149 | }; 150 | /* End PBXGroup section */ 151 | 152 | /* Begin PBXHeadersBuildPhase section */ 153 | 1F4A5C7CE2B0DAC0AC08E864 /* Headers */ = { 154 | isa = PBXHeadersBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | 1F4A571634F8208E3BE1671F /* JustUiKit.h in Headers */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | /* End PBXHeadersBuildPhase section */ 162 | 163 | /* Begin PBXNativeTarget section */ 164 | 1F4A532CDD58114D087EB1A0 /* JustUiKitTests */ = { 165 | isa = PBXNativeTarget; 166 | buildConfigurationList = 1F4A5848BF7D5C2F06C1A7AD /* Build configuration list for PBXNativeTarget "JustUiKitTests" */; 167 | buildPhases = ( 168 | 1F4A585DC44E65ADB54C9A3D /* Sources */, 169 | 1F4A551154CCEE13D44F75EF /* Frameworks */, 170 | 1F4A547AEA28315F6480C19C /* Resources */, 171 | ); 172 | buildRules = ( 173 | ); 174 | dependencies = ( 175 | 1F4A5DAB2D5F2E6BCEC9787E /* PBXTargetDependency */, 176 | ); 177 | name = JustUiKitTests; 178 | productName = JustUiKitTests; 179 | productReference = 529896521E374C0E00CA01B9 /* JustUiKitTests.xctest */; 180 | productType = "com.apple.product-type.bundle.unit-test"; 181 | }; 182 | 1F4A5E9B5B37635B5E156071 /* JustUiKit */ = { 183 | isa = PBXNativeTarget; 184 | buildConfigurationList = 1F4A5597EED025BF554E4B3A /* Build configuration list for PBXNativeTarget "JustUiKit" */; 185 | buildPhases = ( 186 | 1F4A5A2AFF6930AF7DF64C6D /* Sources */, 187 | 1F4A5276196EA8B81244EF6D /* Frameworks */, 188 | 1F4A5C7CE2B0DAC0AC08E864 /* Headers */, 189 | 1F4A5BD68EA21E0D9E3AC6F6 /* Resources */, 190 | ); 191 | buildRules = ( 192 | ); 193 | dependencies = ( 194 | ); 195 | name = JustUiKit; 196 | productName = JustUiKit; 197 | productType = "com.apple.product-type.framework"; 198 | }; 199 | /* End PBXNativeTarget section */ 200 | 201 | /* Begin PBXProject section */ 202 | 1F4A5EE6514429FA6E1E68F6 /* Project object */ = { 203 | isa = PBXProject; 204 | attributes = { 205 | LastSwiftUpdateCheck = 0820; 206 | ORGANIZATIONNAME = "___lfkdsk___"; 207 | }; 208 | buildConfigurationList = 1F4A5518D6076A2787B201A4 /* Build configuration list for PBXProject "JustUiKit" */; 209 | compatibilityVersion = "Xcode 3.2"; 210 | developmentRegion = English; 211 | hasScannedForEncodings = 0; 212 | knownRegions = ( 213 | en, 214 | Base, 215 | ); 216 | mainGroup = 1F4A54665BF1962B972E84B4; 217 | productRefGroup = 1F4A54665BF1962B972E84B4; 218 | projectDirPath = ""; 219 | projectRoot = ""; 220 | targets = ( 221 | 1F4A5E9B5B37635B5E156071 /* JustUiKit */, 222 | 1F4A532CDD58114D087EB1A0 /* JustUiKitTests */, 223 | ); 224 | }; 225 | /* End PBXProject section */ 226 | 227 | /* Begin PBXResourcesBuildPhase section */ 228 | 1F4A547AEA28315F6480C19C /* Resources */ = { 229 | isa = PBXResourcesBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | ); 233 | runOnlyForDeploymentPostprocessing = 0; 234 | }; 235 | 1F4A5BD68EA21E0D9E3AC6F6 /* Resources */ = { 236 | isa = PBXResourcesBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | 1F4A50AB70CA7FAA8452071D /* Info.plist in Resources */, 240 | 1F4A5C1CD4E1B9684E62A2A1 /* .swift-version in Resources */, 241 | 1F4A5BDA99E2B314D8F00C94 /* JustUiKit.podspec in Resources */, 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | }; 245 | /* End PBXResourcesBuildPhase section */ 246 | 247 | /* Begin PBXSourcesBuildPhase section */ 248 | 1F4A585DC44E65ADB54C9A3D /* Sources */ = { 249 | isa = PBXSourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | ); 253 | runOnlyForDeploymentPostprocessing = 0; 254 | }; 255 | 1F4A5A2AFF6930AF7DF64C6D /* Sources */ = { 256 | isa = PBXSourcesBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | 1F4A5D248CDEBE9F7C9D87B3 /* Gravity.swift in Sources */, 260 | 1F4A57E10237CAEE95A4650E /* Orientation.swift in Sources */, 261 | 1F4A5944BBC556272F2CC3A8 /* ColorExtension.swift in Sources */, 262 | 1F4A5478A2C2D99BD50069AC /* JustLinearLayout.swift in Sources */, 263 | 1F4A59E2B441CBC469D1081D /* JustViewGroup.swift in Sources */, 264 | 529896541E374C1B00CA01B9 /* JustRelativeLayout.swift in Sources */, 265 | 1F4A57D0DE7EA6295AB293D2 /* JustViewParent.swift in Sources */, 266 | 1F4A5989D9FC754C62434510 /* JustViewManager.swift in Sources */, 267 | 1F4A52920CBEDAC4E8EBB468 /* JustExtensionView.swift in Sources */, 268 | 1F4A59B149573A55F70F2B95 /* JustLayoutParams.swift in Sources */, 269 | 1F4A50DF28D4F4DB7E48B1F5 /* JustFrameLayout.swift in Sources */, 270 | 1F4A5CC393D38237C20F73B3 /* Queue.swift in Sources */, 271 | 1F4A56BE01BB7181D793C388 /* ReleasePool.swift in Sources */, 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | }; 275 | /* End PBXSourcesBuildPhase section */ 276 | 277 | /* Begin PBXTargetDependency section */ 278 | 1F4A5DAB2D5F2E6BCEC9787E /* PBXTargetDependency */ = { 279 | isa = PBXTargetDependency; 280 | target = 1F4A5E9B5B37635B5E156071 /* JustUiKit */; 281 | targetProxy = 1F4A520233E995710022CAB9 /* PBXContainerItemProxy */; 282 | }; 283 | /* End PBXTargetDependency section */ 284 | 285 | /* Begin XCBuildConfiguration section */ 286 | 1F4A561058B423F883C8864A /* Debug */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | INFOPLIST_FILE = JustUiKitTests/Info.plist; 290 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 291 | PRODUCT_BUNDLE_IDENTIFIER = lfkdsk.JustUiKitTests; 292 | PRODUCT_NAME = "$(TARGET_NAME)"; 293 | SWIFT_VERSION = 3.0; 294 | }; 295 | name = Debug; 296 | }; 297 | 1F4A56538A5E892CBABE47CD /* Release */ = { 298 | isa = XCBuildConfiguration; 299 | buildSettings = { 300 | INFOPLIST_FILE = JustUiKitTests/Info.plist; 301 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 302 | PRODUCT_BUNDLE_IDENTIFIER = lfkdsk.JustUiKitTests; 303 | PRODUCT_NAME = "$(TARGET_NAME)"; 304 | SWIFT_VERSION = 3.0; 305 | }; 306 | name = Release; 307 | }; 308 | 1F4A5816D29D78DADD747432 /* Release */ = { 309 | isa = XCBuildConfiguration; 310 | buildSettings = { 311 | ALWAYS_SEARCH_USER_PATHS = NO; 312 | CLANG_ANALYZER_NONNULL = YES; 313 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 314 | CLANG_CXX_LIBRARY = "libc++"; 315 | CLANG_ENABLE_MODULES = YES; 316 | CLANG_ENABLE_OBJC_ARC = YES; 317 | CLANG_WARN_BOOL_CONVERSION = YES; 318 | CLANG_WARN_CONSTANT_CONVERSION = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 320 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 321 | CLANG_WARN_EMPTY_BODY = YES; 322 | CLANG_WARN_ENUM_CONVERSION = YES; 323 | CLANG_WARN_INFINITE_RECURSION = YES; 324 | CLANG_WARN_INT_CONVERSION = YES; 325 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 326 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 327 | CLANG_WARN_UNREACHABLE_CODE = YES; 328 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 329 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 330 | COPY_PHASE_STRIP = NO; 331 | CURRENT_PROJECT_VERSION = 1; 332 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 333 | ENABLE_NS_ASSERTIONS = NO; 334 | ENABLE_STRICT_OBJC_MSGSEND = YES; 335 | GCC_C_LANGUAGE_STANDARD = gnu99; 336 | GCC_NO_COMMON_BLOCKS = YES; 337 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 338 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 339 | GCC_WARN_UNDECLARED_SELECTOR = YES; 340 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 341 | GCC_WARN_UNUSED_FUNCTION = YES; 342 | GCC_WARN_UNUSED_VARIABLE = YES; 343 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 344 | MTL_ENABLE_DEBUG_INFO = NO; 345 | SDKROOT = iphoneos; 346 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 347 | TARGETED_DEVICE_FAMILY = "1,2"; 348 | VALIDATE_PRODUCT = YES; 349 | VERSIONING_SYSTEM = "apple-generic"; 350 | VERSION_INFO_PREFIX = ""; 351 | }; 352 | name = Release; 353 | }; 354 | 1F4A585421BE7F40D94FA92B /* Debug */ = { 355 | isa = XCBuildConfiguration; 356 | buildSettings = { 357 | CODE_SIGN_IDENTITY = ""; 358 | DEFINES_MODULE = YES; 359 | DYLIB_COMPATIBILITY_VERSION = 1; 360 | DYLIB_CURRENT_VERSION = 1; 361 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 362 | INFOPLIST_FILE = JustUiKit/Info.plist; 363 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 364 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 365 | PRODUCT_BUNDLE_IDENTIFIER = lfkdsk.JustUiKit; 366 | PRODUCT_NAME = "$(TARGET_NAME)"; 367 | SKIP_INSTALL = YES; 368 | SWIFT_VERSION = 3.0; 369 | USER_HEADER_SEARCH_PATHS = "\"${PROJECT_DIR}/JustUiKitTest\"/** \"${PROJECT_DIR}/JustUiKitSample\"/**"; 370 | }; 371 | name = Debug; 372 | }; 373 | 1F4A5A1F0B96416F341B8B1B /* Debug */ = { 374 | isa = XCBuildConfiguration; 375 | buildSettings = { 376 | ALWAYS_SEARCH_USER_PATHS = NO; 377 | CLANG_ANALYZER_NONNULL = YES; 378 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 379 | CLANG_CXX_LIBRARY = "libc++"; 380 | CLANG_ENABLE_MODULES = YES; 381 | CLANG_ENABLE_OBJC_ARC = YES; 382 | CLANG_WARN_BOOL_CONVERSION = YES; 383 | CLANG_WARN_CONSTANT_CONVERSION = YES; 384 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 385 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 386 | CLANG_WARN_EMPTY_BODY = YES; 387 | CLANG_WARN_ENUM_CONVERSION = YES; 388 | CLANG_WARN_INFINITE_RECURSION = YES; 389 | CLANG_WARN_INT_CONVERSION = YES; 390 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 391 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 392 | CLANG_WARN_UNREACHABLE_CODE = YES; 393 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 394 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 395 | COPY_PHASE_STRIP = NO; 396 | CURRENT_PROJECT_VERSION = 1; 397 | DEBUG_INFORMATION_FORMAT = dwarf; 398 | ENABLE_STRICT_OBJC_MSGSEND = YES; 399 | ENABLE_TESTABILITY = YES; 400 | GCC_C_LANGUAGE_STANDARD = gnu99; 401 | GCC_DYNAMIC_NO_PIC = NO; 402 | GCC_NO_COMMON_BLOCKS = YES; 403 | GCC_OPTIMIZATION_LEVEL = 0; 404 | GCC_PREPROCESSOR_DEFINITIONS = ( 405 | "DEBUG=1", 406 | "$(inherited)", 407 | ); 408 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 409 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 410 | GCC_WARN_UNDECLARED_SELECTOR = YES; 411 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 412 | GCC_WARN_UNUSED_FUNCTION = YES; 413 | GCC_WARN_UNUSED_VARIABLE = YES; 414 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 415 | MTL_ENABLE_DEBUG_INFO = YES; 416 | ONLY_ACTIVE_ARCH = YES; 417 | SDKROOT = iphoneos; 418 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 419 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 420 | TARGETED_DEVICE_FAMILY = "1,2"; 421 | VERSIONING_SYSTEM = "apple-generic"; 422 | VERSION_INFO_PREFIX = ""; 423 | }; 424 | name = Debug; 425 | }; 426 | 1F4A5AE4FCB2948544206D6F /* Release */ = { 427 | isa = XCBuildConfiguration; 428 | buildSettings = { 429 | CODE_SIGN_IDENTITY = ""; 430 | DEFINES_MODULE = YES; 431 | DYLIB_COMPATIBILITY_VERSION = 1; 432 | DYLIB_CURRENT_VERSION = 1; 433 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 434 | INFOPLIST_FILE = JustUiKit/Info.plist; 435 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 436 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 437 | PRODUCT_BUNDLE_IDENTIFIER = lfkdsk.JustUiKit; 438 | PRODUCT_NAME = "$(TARGET_NAME)"; 439 | SKIP_INSTALL = YES; 440 | SWIFT_VERSION = 3.0; 441 | USER_HEADER_SEARCH_PATHS = "\"${PROJECT_DIR}/JustUiKitTest\"/** \"${PROJECT_DIR}/JustUiKitSample\"/**"; 442 | }; 443 | name = Release; 444 | }; 445 | /* End XCBuildConfiguration section */ 446 | 447 | /* Begin XCConfigurationList section */ 448 | 1F4A5518D6076A2787B201A4 /* Build configuration list for PBXProject "JustUiKit" */ = { 449 | isa = XCConfigurationList; 450 | buildConfigurations = ( 451 | 1F4A5A1F0B96416F341B8B1B /* Debug */, 452 | 1F4A5816D29D78DADD747432 /* Release */, 453 | ); 454 | defaultConfigurationIsVisible = 0; 455 | defaultConfigurationName = Release; 456 | }; 457 | 1F4A5597EED025BF554E4B3A /* Build configuration list for PBXNativeTarget "JustUiKit" */ = { 458 | isa = XCConfigurationList; 459 | buildConfigurations = ( 460 | 1F4A585421BE7F40D94FA92B /* Debug */, 461 | 1F4A5AE4FCB2948544206D6F /* Release */, 462 | ); 463 | defaultConfigurationIsVisible = 0; 464 | defaultConfigurationName = Release; 465 | }; 466 | 1F4A5848BF7D5C2F06C1A7AD /* Build configuration list for PBXNativeTarget "JustUiKitTests" */ = { 467 | isa = XCConfigurationList; 468 | buildConfigurations = ( 469 | 1F4A561058B423F883C8864A /* Debug */, 470 | 1F4A56538A5E892CBABE47CD /* Release */, 471 | ); 472 | defaultConfigurationIsVisible = 0; 473 | defaultConfigurationName = Release; 474 | }; 475 | /* End XCConfigurationList section */ 476 | }; 477 | rootObject = 1F4A5EE6514429FA6E1E68F6 /* Project object */; 478 | } 479 | -------------------------------------------------------------------------------- /JustUiKit/widget/JustLinearLayout.swift: -------------------------------------------------------------------------------- 1 | /// MIT License 2 | /// 3 | /// Copyright (c) 2017 JustWe 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 | 23 | import UIKit 24 | 25 | public class LinearLayoutParams: MarginLayoutParams { 26 | public var weight: CGFloat = 0 27 | public var layoutGravity: Gravity = Gravity.NO_GRAVITY 28 | public var minHeight: CGFloat = 0 29 | public var minWidth: CGFloat = 0 30 | public var maxHeight: CGFloat = CGFloat.greatestFiniteMagnitude 31 | public var maxWidth: CGFloat = CGFloat.greatestFiniteMagnitude 32 | 33 | override public init(width: CGFloat, height: CGFloat) { 34 | super.init(width: width, height: height) 35 | self.layoutGravity = Gravity.NO_GRAVITY 36 | } 37 | 38 | public init(width: CGFloat, height: CGFloat, layoutGravity: Gravity) { 39 | super.init(width: width, height: height) 40 | self.layoutGravity = layoutGravity 41 | } 42 | 43 | public init(linear: LinearLayoutParams) { 44 | super.init(source: linear) 45 | self.weight = linear.weight 46 | self.layoutGravity = linear.layoutGravity 47 | self.minHeight = linear.minHeight 48 | self.maxHeight = linear.maxHeight 49 | self.minWidth = linear.minWidth 50 | self.maxWidth = linear.maxWidth 51 | } 52 | 53 | override public init(source: MarginLayoutParams) { 54 | super.init(source: source) 55 | } 56 | 57 | override public init(_ params: LayoutParams) { 58 | super.init(params) 59 | } 60 | 61 | public static func generateDefaultParams() -> LinearLayoutParams { 62 | return LinearLayoutParams(width: LayoutParams.WRAP_CONTENT, 63 | height: LayoutParams.WRAP_CONTENT) 64 | } 65 | } 66 | 67 | fileprivate struct Rect { 68 | public var width, height, x, y: CGFloat 69 | 70 | public init(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) { 71 | self.x = x 72 | self.y = y 73 | self.width = width 74 | self.height = height 75 | } 76 | 77 | public init(rect: Rect) { 78 | self.x = rect.x 79 | self.y = rect.y 80 | self.width = rect.width 81 | self.height = rect.height 82 | } 83 | 84 | public func toCGRect() -> CGRect { 85 | return CGRect(x: x, y: y, width: width, height: height) 86 | } 87 | } 88 | 89 | fileprivate extension CGRect { 90 | fileprivate func toRect() -> Rect { 91 | return Rect(x: origin.x, y: origin.y, width: width, height: height) 92 | } 93 | } 94 | 95 | private struct BindViewWithRect { 96 | public var view: UIView 97 | public var rect: Rect 98 | 99 | public init(view: UIView, rect: Rect) { 100 | self.view = view 101 | self.rect = rect 102 | } 103 | 104 | public init(bind: BindViewWithRect) { 105 | self.view = bind.view 106 | self.rect = bind.rect 107 | } 108 | } 109 | 110 | open class JustLinearLayout: JustViewGroup { 111 | private var marginFlag: Gravity = Gravity.TOP | Gravity.LEFT 112 | 113 | private var currentChildLayoutTop: CGFloat = 0.0 114 | private var currentChildLayoutLeft: CGFloat = 0.0 115 | 116 | private var childViewSizes: [CGSize] = [] 117 | 118 | private var orientation: Orientation = Orientation.Vertical { 119 | didSet { 120 | setNeedsLayout() 121 | } 122 | } 123 | 124 | public init(orientation: Orientation) { 125 | super.init() 126 | self.orientation = orientation 127 | } 128 | 129 | public init(frame groupFrame: CGRect, orientation: Orientation) { 130 | super.init(frame: groupFrame) 131 | self.orientation = orientation 132 | } 133 | 134 | public required init?(coder aDecoder: NSCoder) { 135 | super.init(coder: aDecoder) 136 | } 137 | 138 | override open func sizeThatFits(_ size: CGSize) -> CGSize { 139 | let childSizes: [CGSize] 140 | var childLayoutSize = CGSize.zero 141 | 142 | if orientation == .Horizontal { 143 | var padding: Padding 144 | var index: Int = 0 145 | childSizes = measureHorizontal(size) 146 | for size in childSizes { 147 | padding = subviews[index].uiViewExtension.padding 148 | childLayoutSize.width += size.width + padding.paddingLeft + padding.paddingRight 149 | childLayoutSize.height = max(childLayoutSize.height, size.height + padding.paddingTop + padding.paddingBottom) 150 | index += 1 151 | } 152 | } else { 153 | var padding: Padding 154 | var index: Int = 0 155 | childSizes = measureVertical(size) 156 | for size in childSizes { 157 | padding = subviews[index].uiViewExtension.padding 158 | childLayoutSize.width = max(childLayoutSize.width, size.width + padding.paddingLeft + padding.paddingRight) 159 | childLayoutSize.height += size.height + padding.paddingTop + padding.paddingBottom 160 | index += 1 161 | } 162 | } 163 | return uiViewExtension.padding.getMaxSize(size: childLayoutSize) 164 | } 165 | 166 | 167 | public func measureHorizontal(_ size: CGSize) -> [CGSize] { 168 | let selfSize = uiViewExtension.padding.getMinSize(size: size) 169 | 170 | let height = selfSize.height 171 | let width = selfSize.width 172 | 173 | let maxHeightSize: CGFloat = height 174 | var maxWidthSize: CGFloat = width 175 | 176 | var totalWeight: CGFloat = 0.0 177 | 178 | var childSizes = [CGSize](repeating: CGSize(width: -1, height: -1), count: subviews.count) 179 | 180 | for (index, child) in subviews.enumerated() { 181 | if child.isHidden { 182 | continue 183 | } 184 | 185 | let params: LinearLayoutParams = child.uiViewExtension.layoutParams as! LinearLayoutParams 186 | 187 | totalWeight += params.weight 188 | var childHeight: CGFloat = 0 189 | var childSize: CGSize = CGSize() 190 | 191 | switch params.height { 192 | case LayoutParams.MATCH_PARENT: 193 | childHeight = maxHeightSize 194 | case LayoutParams.WRAP_CONTENT: 195 | childSize = child.sizeThatFits( 196 | CGSize(width: maxWidthSize, 197 | height: maxHeightSize)) 198 | 199 | childHeight = childSize.height 200 | default: 201 | childHeight = params.height 202 | } 203 | 204 | childHeight = max(childHeight, params.minHeight) 205 | childHeight = min(childHeight, params.maxHeight) 206 | childHeight = min(childHeight, maxHeightSize) 207 | 208 | childSizes[index].height = childHeight 209 | 210 | if childSize.height == childHeight { 211 | childSizes[index].width = childSize.width 212 | } 213 | 214 | var childWidth: CGFloat = 0 215 | childSize = CGSize.zero 216 | 217 | switch (params.width) { 218 | case LayoutParams.MATCH_PARENT: 219 | childWidth = maxWidthSize 220 | case LayoutParams.WRAP_CONTENT: 221 | childSize = child.sizeThatFits( 222 | CGSize(width: maxWidthSize, 223 | height: maxHeightSize)) 224 | 225 | childWidth = childSize.width 226 | default: 227 | childWidth = params.width 228 | } 229 | 230 | childWidth = max(childWidth, params.minWidth) 231 | childWidth = min(childWidth, params.maxWidth) 232 | childWidth = min(childWidth, maxWidthSize) 233 | 234 | childSizes[index].width = childWidth 235 | 236 | maxWidthSize -= (childWidth) 237 | } 238 | 239 | if totalWeight > 0.0 { 240 | let layoutEx = uiViewExtension.padding 241 | maxWidthSize = width - layoutEx.paddingLeft - layoutEx.paddingRight 242 | for (index, child) in subviews.enumerated() { 243 | if child.isHidden { 244 | continue 245 | } 246 | 247 | let params: LinearLayoutParams = child.uiViewExtension.layoutParams as! LinearLayoutParams 248 | 249 | if params.weight > 0.0 && maxWidthSize > 0.0 { 250 | let childWidth: CGFloat = params.weight * maxWidthSize / totalWeight 251 | if childWidth > 0.0 { 252 | childSizes[index].width = childWidth 253 | continue 254 | } 255 | } else { 256 | childSizes[index].height = 0 257 | childSizes[index].width = 0 258 | } 259 | } 260 | } 261 | 262 | return childSizes 263 | } 264 | 265 | private func measureVertical(_ size: CGSize) -> [CGSize] { 266 | let selfSize = uiViewExtension.padding.getMinSize(size: size) 267 | 268 | let height = selfSize.height 269 | let width = selfSize.width 270 | 271 | let layoutEx = uiViewExtension.padding 272 | 273 | var maxHeightSize: CGFloat = height 274 | let maxWidthSize: CGFloat = width 275 | 276 | var totalWeight: CGFloat = 0.0 277 | 278 | // all sub view size 279 | var childSizes = [CGSize](repeating: CGSize(width: -1, height: -1), count: subviews.count) 280 | 281 | for (index, child) in subviews.enumerated() { 282 | if (child.isHidden) { 283 | continue 284 | } 285 | 286 | let params: LinearLayoutParams = child.uiViewExtension.layoutParams as! LinearLayoutParams 287 | 288 | totalWeight += params.weight 289 | 290 | var childWidth: CGFloat = 0 291 | var childSize: CGSize = CGSize() 292 | 293 | switch (params.width) { 294 | case LayoutParams.MATCH_PARENT: 295 | childWidth = maxWidthSize 296 | case LayoutParams.WRAP_CONTENT: 297 | childSize = child.sizeThatFits( 298 | CGSize(width: maxWidthSize, 299 | height: maxHeightSize)) 300 | 301 | childWidth = childSize.width 302 | default: 303 | childWidth = params.width 304 | } 305 | 306 | childWidth = max(childWidth, params.minWidth) 307 | childWidth = min(childWidth, params.maxWidth) 308 | childWidth = min(childWidth, maxWidthSize) 309 | 310 | childSizes[index].width = childWidth 311 | 312 | if childSize.width == childWidth { 313 | childSizes[index].height = childSize.height 314 | } 315 | 316 | 317 | // maxWidthSize -= (childWidth + marginWidth) 318 | 319 | //////////////////////////////////////////// 320 | var childHeight: CGFloat = 0 321 | childSize = CGSize() 322 | 323 | switch params.height { 324 | case LayoutParams.MATCH_PARENT: 325 | childHeight = maxHeightSize 326 | case LayoutParams.WRAP_CONTENT: 327 | childSize = child.sizeThatFits( 328 | CGSize(width: maxWidthSize, 329 | height: maxHeightSize)) 330 | 331 | childHeight = childSize.height 332 | default: 333 | childHeight = params.height 334 | } 335 | 336 | childHeight = max(childHeight, params.minHeight) 337 | childHeight = min(childHeight, params.maxHeight) 338 | childHeight = min(childHeight, maxHeightSize) 339 | 340 | childSizes[index].height = childHeight 341 | 342 | maxHeightSize -= (childHeight) 343 | } 344 | 345 | if totalWeight > 0.0 { 346 | 347 | maxHeightSize = height - layoutEx.paddingTop - layoutEx.paddingBottom 348 | 349 | for (index, child) in subviews.enumerated() { 350 | if child.isHidden { 351 | continue 352 | } 353 | 354 | let params: LinearLayoutParams = child.uiViewExtension.layoutParams as! LinearLayoutParams 355 | 356 | if params.weight > 0.0 && maxHeightSize > 0.0 { 357 | let childHeight: CGFloat = params.weight * maxHeightSize / totalWeight 358 | if childHeight > 0.0 { 359 | childSizes[index].height = childHeight 360 | continue 361 | } 362 | } else { 363 | childSizes[index].height = 0 364 | childSizes[index].width = 0 365 | } 366 | } 367 | } 368 | 369 | return childSizes 370 | } 371 | 372 | public func layoutVertical(_ top: CGFloat, 373 | _ left: CGFloat, 374 | _ right: CGFloat, 375 | _ bottom: CGFloat) { 376 | // let height = bottom - top 377 | // let padding = uiViewExtension.padding 378 | 379 | currentChildLayoutTop = top 380 | 381 | var childViewRect: [BindViewWithRect] = [] 382 | 383 | for (index, size) in childViewSizes.enumerated() { 384 | 385 | let child = subviews[index] 386 | 387 | if child.isHidden { 388 | continue 389 | } 390 | 391 | let childParams: LinearLayoutParams = child.uiViewExtension.layoutParams as! LinearLayoutParams 392 | let childWidth = size.width 393 | let childHeight = size.height 394 | var childTop: CGFloat = 0 395 | var childLeft: CGFloat = 0 396 | 397 | // let verticalGravity = childParams.layoutGravity & JustLinearLayout.VERTICAL_GRAVITY_MASK 398 | // 399 | // var specialComFlag = false 400 | 401 | // switch verticalGravity { 402 | // case Gravity.CENTER_VERTICAL.getValue(): 403 | // childTop = (height - childHeight) / 2 404 | // childTop += CGFloat(childParams.topMargin - childParams.bottomMargin) 405 | // case Gravity.BOTTOM.getValue(): 406 | // specialComFlag = true 407 | // childTop = height - childHeight 408 | // default: 409 | // childTop = currentChildLayoutTop + CGFloat(childParams.topMargin) 410 | // } 411 | childTop = currentChildLayoutTop + CGFloat(childParams.topMargin) 412 | 413 | let horizontalGravity = Gravity.getHorizontalGravity(gravity: childParams.layoutGravity) 414 | 415 | switch horizontalGravity { 416 | case Gravity.CENTER_HORIZONTAL.getValue(): 417 | // if verticalGravity == Gravity.CENTER_VERTICAL.getValue() 418 | // || verticalGravity == Gravity.BOTTOM.getValue() { 419 | // specialComFlag = true 420 | // } 421 | childLeft = (right - left - childWidth) / 2 + left 422 | childLeft += (CGFloat(childParams.leftMargin - childParams.rightMargin)) 423 | case Gravity.RIGHT.getValue(): 424 | // if verticalGravity == Gravity.BOTTOM.getValue() { 425 | // specialComFlag = true 426 | // } 427 | childLeft = right - left - childWidth - CGFloat(childParams.rightMargin); 428 | default: 429 | childLeft = left + CGFloat(childParams.leftMargin) 430 | // if hasParent() && verticalGravity == Gravity.BOTTOM.getValue() { 431 | // childLeft -= left 432 | // } 433 | } 434 | 435 | 436 | if hasParent() { 437 | childTop -= top 438 | childLeft -= left 439 | } 440 | 441 | // if childViewRect.count == 0 { 442 | // childLeft += padding.paddingLeft 443 | // childTop += padding.paddingTop 444 | // } 445 | 446 | child.frame = CGRect( 447 | x: childLeft, 448 | y: childTop, 449 | width: childWidth, 450 | height: childHeight) 451 | child.layoutSubviews() 452 | 453 | childViewRect.append(BindViewWithRect( 454 | view: child, 455 | rect: child.frame.toRect())) 456 | 457 | // if !specialComFlag { 458 | currentChildLayoutTop += size.height 459 | // } 460 | } 461 | 462 | var localBinders: [BindViewWithRect] = childViewRect.sorted { (first, second) -> Bool in 463 | return first.rect.y < second.rect.y 464 | } 465 | 466 | var index: Int = 0 467 | 468 | for item in localBinders { 469 | 470 | if index == localBinders.endIndex - 1 { 471 | break 472 | } 473 | 474 | var nextView = localBinders[index + 1] 475 | let itemBottom = item.rect.y + item.rect.height 476 | let nextTop = nextView.rect.y 477 | 478 | if nextTop <= itemBottom { 479 | let oldFrame = nextView.rect 480 | 481 | nextView.rect = Rect( 482 | x: oldFrame.x, 483 | y: itemBottom, 484 | width: oldFrame.width, 485 | height: oldFrame.height 486 | ) 487 | nextView.view.frame = nextView.rect.toCGRect() 488 | nextView.view.layoutSubviews() 489 | } 490 | index += 1 491 | } 492 | } 493 | 494 | 495 | public func layoutHorizontal(_ top: CGFloat, 496 | _ left: CGFloat, 497 | _ right: CGFloat, 498 | _ bottom: CGFloat) { 499 | // let width = right - left 500 | let height = bottom - top 501 | // let padding = uiViewExtension.padding 502 | 503 | currentChildLayoutLeft = left 504 | 505 | var childViewRect: [BindViewWithRect] = [] 506 | 507 | for (index, size) in childViewSizes.enumerated() { 508 | 509 | let child = subviews[index] 510 | 511 | if child.isHidden { 512 | continue 513 | } 514 | 515 | let childParams: LinearLayoutParams = child.uiViewExtension.layoutParams as! LinearLayoutParams 516 | let childWidth = size.width 517 | let childHeight = size.height 518 | var childTop: CGFloat = 0 519 | var childLeft: CGFloat = 0 520 | 521 | // let horizontalGravity = childParams.layoutGravity & JustLinearLayout.HORIZONTAL_GRAVITY_MASK 522 | // 523 | // var specialComFlag = false 524 | // 525 | // switch horizontalGravity { 526 | // case Gravity.RIGHT.getValue(): 527 | // childLeft = width - childWidth - CGFloat(childParams.rightMargin); 528 | // case Gravity.CENTER_HORIZONTAL.getValue(): 529 | // childLeft = (right - left - childWidth) / 2 530 | // childLeft += (CGFloat(childParams.leftMargin - childParams.rightMargin)) 531 | // default: 532 | // childLeft = currentChildLayoutLeft + CGFloat(childParams.leftMargin) 533 | // } 534 | 535 | childLeft = currentChildLayoutLeft + CGFloat(childParams.leftMargin) 536 | 537 | let verticalGravity = Gravity.getVerticalGravity(gravity: childParams.layoutGravity) 538 | 539 | switch verticalGravity { 540 | case Gravity.CENTER_VERTICAL.getValue(): 541 | childTop = (height - childHeight) / 2 + top 542 | childTop += CGFloat(childParams.topMargin - childParams.bottomMargin) 543 | case Gravity.BOTTOM.getValue(): 544 | childTop = bottom - childHeight 545 | default: 546 | childTop = top + CGFloat(childParams.topMargin) 547 | } 548 | 549 | if hasParent() { 550 | childTop -= top 551 | childLeft -= left 552 | } 553 | // 554 | // if childViewRect.count == 0 { 555 | // childLeft += padding.paddingLeft 556 | // childTop += padding.paddingTop 557 | // } 558 | 559 | child.frame = CGRect( 560 | x: childLeft, 561 | y: childTop, 562 | width: childWidth, 563 | height: childHeight) 564 | child.layoutSubviews() 565 | 566 | // if !specialComFlag { 567 | currentChildLayoutLeft += size.width 568 | // } 569 | 570 | childViewRect.append(BindViewWithRect( 571 | view: child, 572 | rect: child.frame.toRect())) 573 | } 574 | 575 | var localBinders: [BindViewWithRect] = childViewRect.sorted { (first, second) -> Bool in 576 | return first.rect.x < second.rect.x 577 | } 578 | 579 | var index: Int = 0 580 | 581 | for item in localBinders { 582 | 583 | if index == localBinders.endIndex - 1 { 584 | break 585 | } 586 | 587 | var nextView = localBinders[index + 1] 588 | let itemRight = item.rect.x + item.rect.width 589 | let nextLeft = nextView.rect.x 590 | 591 | if nextLeft <= itemRight { 592 | let oldFrame = nextView.rect 593 | 594 | nextView.rect = Rect( 595 | x: itemRight, 596 | y: oldFrame.y, 597 | width: oldFrame.width, 598 | height: oldFrame.height 599 | ) 600 | nextView.view.frame = nextView.rect.toCGRect() 601 | nextView.view.layoutSubviews() 602 | } 603 | index += 1 604 | } 605 | } 606 | 607 | override public func onLayout(_ changed: Bool, 608 | _ top: CGFloat, 609 | _ left: CGFloat, 610 | _ right: CGFloat, 611 | _ bottom: CGFloat) { 612 | super.onLayout(changed, top, left, right, bottom) 613 | let padding = uiViewExtension.padding 614 | 615 | let pTop = top + padding.paddingTop, 616 | pLeft = left + padding.paddingLeft, 617 | pRight = right - padding.paddingRight, 618 | pBottom = bottom - padding.paddingBottom 619 | 620 | if orientation == Orientation.Horizontal { 621 | layoutHorizontal( 622 | pTop, pLeft, pRight, pBottom) 623 | } else { 624 | layoutVertical( 625 | pTop, pLeft, pRight, pBottom) 626 | } 627 | } 628 | 629 | override public func onMeasure(_ size: CGSize) { 630 | super.onMeasure(size) 631 | 632 | if orientation == Orientation.Horizontal { 633 | childViewSizes = measureHorizontal(size) 634 | } else { 635 | childViewSizes = measureVertical(size) 636 | } 637 | } 638 | 639 | public func addView(view: UIView, params: LinearLayoutParams = LinearLayoutParams.generateDefaultParams()) { 640 | view.uiViewExtension.layoutParams = params 641 | 642 | if view.superview != nil { 643 | return 644 | } 645 | 646 | if view is JustViewGroup { 647 | (view as! JustViewGroup).setParent(viewGroup: self) 648 | } 649 | 650 | addSubview(view) 651 | params.bindWith(view: view) 652 | } 653 | 654 | override public func addView(view: UIView) { 655 | super.addView(view: view) 656 | self.addView(view: view, params: LinearLayoutParams.generateDefaultParams()) 657 | } 658 | 659 | } 660 | -------------------------------------------------------------------------------- /JustUiKit/widget/JustRelativeLayout.swift: -------------------------------------------------------------------------------- 1 | /// MIT License 2 | /// 3 | /// Copyright (c) 2017 JustWe 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 | 23 | import UIKit 24 | import Foundation 25 | 26 | public enum RelativeRule: Int { 27 | case NONE_RULE = -1 28 | /** 29 | * Rule that aligns a child's right edge with another child's left edge. 30 | */ 31 | case LEFT_OF = 0; 32 | /** 33 | * Rule that aligns a child's left edge with another child's right edge. 34 | */ 35 | case RIGHT_OF = 1; 36 | /** 37 | * Rule that aligns a child's bottom edge with another child's top edge. 38 | */ 39 | case ABOVE = 2; 40 | /** 41 | * Rule that aligns a child's top edge with another child's bottom edge. 42 | */ 43 | case BELOW = 3; 44 | 45 | /** 46 | * Rule that aligns a child's baseline with another child's baseline. 47 | */ 48 | case ALIGN_BASELINE = 4; 49 | /** 50 | * Rule that aligns a child's left edge with another child's left edge. 51 | */ 52 | case ALIGN_LEFT = 5; 53 | /** 54 | * Rule that aligns a child's top edge with another child's top edge. 55 | */ 56 | case ALIGN_TOP = 6; 57 | /** 58 | * Rule that aligns a child's right edge with another child's right edge. 59 | */ 60 | case ALIGN_RIGHT = 7; 61 | /** 62 | * Rule that aligns a child's bottom edge with another child's bottom edge. 63 | */ 64 | case ALIGN_BOTTOM = 8; 65 | 66 | /** 67 | * Rule that aligns the child's left edge with its RelativeLayout 68 | * parent's left edge. 69 | */ 70 | case ALIGN_PARENT_LEFT = 9; 71 | /** 72 | * Rule that aligns the child's top edge with its RelativeLayout 73 | * parent's top edge. 74 | */ 75 | case ALIGN_PARENT_TOP = 10; 76 | /** 77 | * Rule that aligns the child's right edge with its RelativeLayout 78 | * parent's right edge. 79 | */ 80 | case ALIGN_PARENT_RIGHT = 11; 81 | /** 82 | * Rule that aligns the child's bottom edge with its RelativeLayout 83 | * parent's bottom edge. 84 | */ 85 | case ALIGN_PARENT_BOTTOM = 12; 86 | 87 | /** 88 | * Rule that centers the child with respect to the bounds of its 89 | * RelativeLayout parent. 90 | */ 91 | case CENTER_IN_PARENT = 13; 92 | /** 93 | * Rule that centers the child horizontally with respect to the 94 | * bounds of its RelativeLayout parent. 95 | */ 96 | case CENTER_HORIZONTAL = 14; 97 | /** 98 | * Rule that centers the child vertically with respect to the 99 | * bounds of its RelativeLayout parent. 100 | */ 101 | case CENTER_VERTICAL = 15; 102 | 103 | fileprivate static let VERB_COUNT = 16; 104 | 105 | fileprivate static let RULES_VERTICAL = [ 106 | ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM 107 | ] 108 | 109 | fileprivate static let RULES_HORIZONTAL = [ 110 | LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT 111 | ] 112 | 113 | public func getValue() -> Int { 114 | return rawValue 115 | } 116 | } 117 | 118 | fileprivate enum BindType { 119 | case UNBIND 120 | case BIND 121 | } 122 | 123 | 124 | fileprivate struct BindViewWithRule { 125 | var RULE: RelativeRule = .NONE_RULE 126 | var VIEW: UIView? 127 | var TYPE: BindType = .UNBIND 128 | 129 | init(view: UIView, rule: RelativeRule) { 130 | RULE = rule 131 | VIEW = view 132 | TYPE = .BIND 133 | } 134 | 135 | private init() { 136 | 137 | } 138 | 139 | fileprivate static func generateDefaultBind() -> BindViewWithRule { 140 | return BindViewWithRule() 141 | } 142 | } 143 | 144 | public class RelativeLayoutParams: MarginLayoutParams { 145 | 146 | fileprivate var rules: [BindViewWithRule] = 147 | [BindViewWithRule]( 148 | repeating: BindViewWithRule.generateDefaultBind(), 149 | count: RelativeRule.VERB_COUNT) 150 | 151 | public var layoutGravity: Gravity = Gravity.NO_GRAVITY 152 | 153 | public static let VALUE_NOT_SET: CGFloat = -1 154 | 155 | public var mLeft: CGFloat = VALUE_NOT_SET, 156 | mTop: CGFloat = VALUE_NOT_SET, 157 | mRight: CGFloat = VALUE_NOT_SET, 158 | mBottom: CGFloat = VALUE_NOT_SET 159 | 160 | public var alignWithParent: Bool = false 161 | 162 | override public init(_ source: LayoutParams) { 163 | super.init(source) 164 | } 165 | 166 | override public init(width: CGFloat, height: CGFloat) { 167 | super.init(width: width, height: height) 168 | } 169 | 170 | fileprivate func getRules() -> [BindViewWithRule] { 171 | return rules 172 | } 173 | 174 | public func addRule(rule: RelativeRule, view: UIView) { 175 | rules[rule.getValue()].VIEW = view 176 | rules[rule.getValue()].RULE = rule 177 | rules[rule.getValue()].TYPE = .BIND 178 | self.setAlignParentValue(rule, true) 179 | } 180 | 181 | public func addRule(rule: RelativeRule) { 182 | rules[rule.getValue()].RULE = rule 183 | rules[rule.getValue()].TYPE = .BIND 184 | self.setAlignParentValue(rule, true) 185 | } 186 | 187 | public func removeRule(rule: RelativeRule) { 188 | rules[rule.getValue()] = BindViewWithRule.generateDefaultBind() 189 | removeAlignParentValue() 190 | } 191 | 192 | private func setAlignParentValue(_ rule: RelativeRule, 193 | _ value: Bool) { 194 | switch rule { 195 | case .ALIGN_PARENT_RIGHT, .ALIGN_PARENT_LEFT, .ALIGN_PARENT_TOP, .ALIGN_PARENT_BOTTOM: 196 | self.alignWithParent = value 197 | default: 198 | break 199 | } 200 | } 201 | 202 | private func removeAlignParentValue() { 203 | var hasParentRule: Bool = false 204 | loop: for item in rules { 205 | switch item.RULE { 206 | case .ALIGN_PARENT_RIGHT, .ALIGN_PARENT_LEFT, .ALIGN_PARENT_TOP, .ALIGN_PARENT_BOTTOM: 207 | hasParentRule = true 208 | break loop 209 | default: 210 | continue 211 | } 212 | } 213 | 214 | self.alignWithParent = hasParentRule 215 | } 216 | 217 | public func alignParentTop() { 218 | addRule(rule: .ALIGN_PARENT_TOP) 219 | } 220 | 221 | public func alignParentLeft() { 222 | addRule(rule: .ALIGN_PARENT_LEFT) 223 | } 224 | 225 | public func alignParentBottom() { 226 | addRule(rule: .ALIGN_BOTTOM) 227 | } 228 | 229 | public func alignParentRight() { 230 | addRule(rule: .ALIGN_PARENT_RIGHT) 231 | } 232 | 233 | public func alignTopTo(top: UIView) { 234 | addRule(rule: .ALIGN_TOP, view: top) 235 | } 236 | 237 | public func alignLeftTo(left: UIView) { 238 | addRule(rule: .ALIGN_LEFT, view: left) 239 | } 240 | 241 | public func alignRightTo(right: UIView) { 242 | addRule(rule: .ALIGN_RIGHT, view: right) 243 | } 244 | 245 | public func alignBottomTo(bottom: UIView) { 246 | addRule(rule: .ALIGN_BOTTOM, view: bottom) 247 | } 248 | 249 | public func rightOf(_ target: UIView) { 250 | addRule(rule: .RIGHT_OF, view: target) 251 | } 252 | 253 | public func leftOf(_ target: UIView) { 254 | addRule(rule: .LEFT_OF, view: target) 255 | } 256 | 257 | public func aboveOf(_ target: UIView) { 258 | addRule(rule: .ABOVE, view: target) 259 | } 260 | 261 | public func bellowOf(_ target: UIView) { 262 | addRule(rule: .BELOW, view: target) 263 | } 264 | 265 | public func centerInParent() { 266 | addRule(rule: .CENTER_IN_PARENT) 267 | layoutGravity = layoutGravity | Gravity.CENTER 268 | } 269 | 270 | public func centerInHorizontal() { 271 | addRule(rule: .CENTER_HORIZONTAL) 272 | layoutGravity = layoutGravity | Gravity.CENTER_VERTICAL 273 | } 274 | 275 | public func centerInVertical() { 276 | addRule(rule: .CENTER_VERTICAL) 277 | layoutGravity = layoutGravity | Gravity.CENTER_HORIZONTAL 278 | } 279 | 280 | public static func generateDefaultParams() -> RelativeLayoutParams { 281 | return RelativeLayoutParams(width: LayoutParams.WRAP_CONTENT, 282 | height: LayoutParams.WRAP_CONTENT) 283 | } 284 | } 285 | 286 | open class JustRelativeLayout: JustViewGroup { 287 | 288 | private var mSortedHorizontalChildren: [UIView] = [] 289 | 290 | private var mSortedVerticalChildren: [UIView] = [] 291 | 292 | private var mMeasuredView: [CGSize] = [] 293 | 294 | private var mDGraph: DependencyGraph = DependencyGraph() 295 | 296 | private var mDirtyFresh = true 297 | 298 | private var layoutSize = CGSize.zero 299 | 300 | override open func sizeThatFits(_ size: CGSize) -> CGSize { 301 | var childLayoutSize: CGSize = CGSize.zero 302 | childLayoutSize = measureChild(size) 303 | return uiViewExtension.padding.getMaxSize(size: childLayoutSize) 304 | } 305 | 306 | override public func onLayout(_ changed: Bool, _ top: CGFloat, _ left: CGFloat, _ right: CGFloat, _ bottom: CGFloat) { 307 | super.onLayout(changed, top, left, right, bottom) 308 | 309 | for (_, child) in subviews.enumerated() { 310 | 311 | if child.isHidden { 312 | continue 313 | } 314 | 315 | let params = child.uiViewExtension.layoutParams as! RelativeLayoutParams 316 | 317 | child.frame = CGRect( 318 | x: params.mLeft, 319 | y: params.mTop, 320 | width: params.width, 321 | height: params.height) 322 | child.layoutSubviews() 323 | } 324 | } 325 | 326 | override public func onMeasure(_ size: CGSize) { 327 | super.onMeasure(size) 328 | layoutSize = measureChild(size) 329 | } 330 | 331 | private func measureChild(_ size: CGSize) -> CGSize { 332 | 333 | let selfSize = uiViewExtension.padding.getMinSize(size: size) 334 | 335 | let padding = uiViewExtension.padding 336 | 337 | let pTop = frame.origin.y + padding.paddingTop, 338 | pLeft = frame.origin.x + padding.paddingLeft, 339 | pRight = pLeft + size.width - padding.paddingRight, 340 | pBottom = pTop + size.height - padding.paddingBottom 341 | 342 | let height = selfSize.height 343 | let width = selfSize.width 344 | 345 | var minTop: CGFloat = 0 346 | var minLeft: CGFloat = 0 347 | var maxRight: CGFloat = 0 348 | var maxBottom: CGFloat = 0 349 | 350 | if mDirtyFresh { 351 | mDirtyFresh = false 352 | sortChildren() 353 | } 354 | 355 | var views: [UIView] = mSortedHorizontalChildren 356 | var count = views.count 357 | 358 | // initial params with first unhidden view 359 | var initFlag: Bool = true 360 | 361 | for index in 0 ..< count { 362 | 363 | let child: UIView = views[index] 364 | 365 | if child.isHidden { 366 | continue 367 | } 368 | 369 | let params: RelativeLayoutParams = child.getLayoutParams() as! RelativeLayoutParams 370 | var rules: [BindViewWithRule] = params.getRules() 371 | 372 | applyHorizontalSizeRules(params: params, width: width, rules: &rules) 373 | measureChildHorizontal(view: child, width: width, height: height) 374 | positionHorizontalChildViews(child: child, left: pLeft, right: pRight) 375 | 376 | if initFlag { 377 | initFlag = false 378 | minTop = params.mTop 379 | minLeft = params.mLeft 380 | maxBottom = params.mBottom 381 | maxRight = params.mRight 382 | } 383 | 384 | minTop = min(minTop, params.mTop) 385 | minLeft = min(minLeft, params.mLeft) 386 | maxBottom = max(maxBottom, params.mBottom) 387 | maxRight = max(maxBottom, params.mRight) 388 | } 389 | 390 | views = mSortedVerticalChildren 391 | count = views.count 392 | 393 | for index in 0 ..< count { 394 | let child: UIView = views[index] 395 | 396 | if child.isHidden { 397 | continue 398 | } 399 | 400 | let params: RelativeLayoutParams = child.getLayoutParams() as! RelativeLayoutParams 401 | var rules: [BindViewWithRule] = params.getRules() 402 | 403 | applyVerticalSizeRules(params: params, height: height, rules: &rules) 404 | measureChildVertical(view: child, width: width, height: height) 405 | positionVerticalChildViews(child: child, top: pTop, bottom: pBottom) 406 | 407 | minTop = min(minTop, params.mTop) 408 | minLeft = min(minLeft, params.mLeft) 409 | maxBottom = max(maxBottom, params.mBottom) 410 | maxRight = max(maxRight, params.mRight) 411 | } 412 | 413 | return CGSize(width: maxRight - minLeft, height: maxBottom - minTop) 414 | } 415 | 416 | private func applyVerticalSizeRules(params childParams: RelativeLayoutParams, height: CGFloat, 417 | rules: inout [BindViewWithRule]) { 418 | func hasBindView(rule: RelativeRule) -> Bool { 419 | let ruleValue: BindViewWithRule = rules[rule.getValue()] 420 | return ruleValue.VIEW != nil && ruleValue.TYPE == BindType.BIND 421 | } 422 | 423 | var anchorParams: RelativeLayoutParams? 424 | 425 | childParams.mTop = RelativeLayoutParams.VALUE_NOT_SET 426 | childParams.mBottom = RelativeLayoutParams.VALUE_NOT_SET 427 | 428 | anchorParams = getRelatedViewParams(rules: &rules, relation: .ABOVE) 429 | 430 | if anchorParams != nil { 431 | childParams.mBottom = anchorParams!.mTop - CGFloat(anchorParams!.topMargin + 432 | childParams.bottomMargin) 433 | } else if childParams.alignWithParent && hasBindView(rule: .ABOVE) { 434 | if (height >= 0) { 435 | childParams.mBottom = height - CGFloat(childParams.bottomMargin) 436 | } 437 | } 438 | 439 | anchorParams = getRelatedViewParams(rules: &rules, relation: .BELOW) 440 | 441 | if anchorParams != nil { 442 | childParams.mTop = anchorParams!.mBottom + CGFloat(anchorParams!.bottomMargin + 443 | childParams.topMargin); 444 | } else if childParams.alignWithParent && hasBindView(rule: .BELOW) { 445 | childParams.mTop = CGFloat(childParams.topMargin) 446 | } 447 | 448 | anchorParams = getRelatedViewParams(rules: &rules, relation: .ALIGN_TOP) 449 | 450 | if anchorParams != nil { 451 | childParams.mTop = anchorParams!.mTop + CGFloat(childParams.topMargin) 452 | } else if childParams.alignWithParent && hasBindView(rule: .ALIGN_TOP) { 453 | childParams.mTop = CGFloat(childParams.topMargin) 454 | } 455 | 456 | anchorParams = getRelatedViewParams(rules: &rules, relation: .ALIGN_BOTTOM) 457 | 458 | if anchorParams != nil { 459 | childParams.mBottom = anchorParams!.mBottom - CGFloat(childParams.bottomMargin) 460 | } else if childParams.alignWithParent && hasBindView(rule: .ALIGN_BOTTOM) { 461 | if (height >= 0) { 462 | childParams.mBottom = height - CGFloat(childParams.bottomMargin) 463 | } 464 | } 465 | 466 | if hasBindView(rule: .ALIGN_PARENT_TOP) { 467 | childParams.mTop = CGFloat(childParams.topMargin) 468 | } 469 | 470 | if hasBindView(rule: .ALIGN_PARENT_BOTTOM) { 471 | if (height >= 0) { 472 | childParams.mBottom = height - CGFloat(childParams.bottomMargin) 473 | } 474 | } 475 | } 476 | 477 | private func measureChildVertical(view: UIView, 478 | width: CGFloat, 479 | height: CGFloat) { 480 | var childWidth: CGFloat = 0 481 | var childSize: CGSize = CGSize() 482 | let params: RelativeLayoutParams = view.uiViewExtension.layoutParams as! RelativeLayoutParams 483 | 484 | switch (params.width) { 485 | case LayoutParams.MATCH_PARENT: 486 | childWidth = width 487 | case LayoutParams.WRAP_CONTENT: 488 | childSize = view.sizeThatFits( 489 | CGSize(width: width, 490 | height: height)) 491 | 492 | childWidth = childSize.width 493 | default: 494 | childWidth = params.width 495 | } 496 | 497 | childWidth = min(childWidth, width) 498 | 499 | params.width = childWidth 500 | } 501 | 502 | private func measureChildHorizontal(view: UIView, 503 | width: CGFloat, 504 | height: CGFloat) { 505 | let params: RelativeLayoutParams = view.uiViewExtension.layoutParams as! RelativeLayoutParams 506 | 507 | var childHeight: CGFloat = 0 508 | var childSize: CGSize = CGSize() 509 | 510 | switch params.height { 511 | case LayoutParams.MATCH_PARENT: 512 | childHeight = width 513 | case LayoutParams.WRAP_CONTENT: 514 | childSize = view.sizeThatFits( 515 | CGSize(width: width, 516 | height: height)) 517 | childHeight = childSize.height 518 | default: 519 | childHeight = params.height 520 | } 521 | 522 | childHeight = min(childHeight, height) 523 | params.height = childHeight 524 | 525 | if childSize.height == childHeight { 526 | params.width = childSize.width 527 | } 528 | } 529 | 530 | public func positionVerticalChildViews(child: UIView, 531 | top: CGFloat, 532 | bottom: CGFloat) { 533 | 534 | let childParams: RelativeLayoutParams = child.uiViewExtension.layoutParams as! RelativeLayoutParams 535 | 536 | if childParams.mTop == RelativeLayoutParams.VALUE_NOT_SET && childParams.mBottom != RelativeLayoutParams.VALUE_NOT_SET { 537 | // set mLeft 538 | childParams.mTop = childParams.mBottom - childParams.height 539 | } else if childParams.mTop != RelativeLayoutParams.VALUE_NOT_SET && childParams.mBottom == RelativeLayoutParams.VALUE_NOT_SET { 540 | childParams.mBottom = childParams.mTop + childParams.height 541 | } else if childParams.mTop == RelativeLayoutParams.VALUE_NOT_SET && childParams.mBottom == RelativeLayoutParams.VALUE_NOT_SET { 542 | let verticalGravity = Gravity.getVerticalGravity(gravity: childParams.layoutGravity) 543 | let childHeight = childParams.height 544 | switch verticalGravity { 545 | case Gravity.CENTER_VERTICAL.getValue(): 546 | childParams.mTop = (bottom - top - childHeight) / 2 547 | childParams.mTop += CGFloat(childParams.topMargin - childParams.bottomMargin) 548 | case Gravity.BOTTOM.getValue(): 549 | childParams.mTop = bottom - top - childHeight 550 | default: 551 | childParams.mTop = top + CGFloat(childParams.topMargin) 552 | } 553 | childParams.mBottom = childParams.mTop + childParams.height 554 | } 555 | } 556 | 557 | public func positionHorizontalChildViews(child: UIView, 558 | left: CGFloat, 559 | right: CGFloat) { 560 | 561 | let childParams: RelativeLayoutParams = child.uiViewExtension.layoutParams as! RelativeLayoutParams 562 | let childWidth = childParams.width 563 | 564 | if childParams.mLeft == RelativeLayoutParams.VALUE_NOT_SET && childParams.mRight != RelativeLayoutParams.VALUE_NOT_SET { 565 | // set mLeft 566 | childParams.mLeft = childParams.mRight - childParams.width 567 | } else if childParams.mLeft != RelativeLayoutParams.VALUE_NOT_SET && childParams.mRight == RelativeLayoutParams.VALUE_NOT_SET { 568 | childParams.mRight = childParams.mLeft + childParams.height 569 | } else if childParams.mLeft == RelativeLayoutParams.VALUE_NOT_SET && childParams.mRight == RelativeLayoutParams.VALUE_NOT_SET { 570 | let horizontalGravity = Gravity.getHorizontalGravity(gravity: childParams.layoutGravity) 571 | switch horizontalGravity { 572 | case Gravity.CENTER_HORIZONTAL.getValue(): 573 | childParams.mLeft = (right - left - childWidth) / 2 + left 574 | childParams.mLeft += (CGFloat(childParams.leftMargin - childParams.rightMargin)) 575 | case Gravity.RIGHT.getValue(): 576 | childParams.mLeft = right - left - childWidth - CGFloat(childParams.rightMargin) 577 | default: 578 | childParams.mLeft = left + CGFloat(childParams.leftMargin) 579 | } 580 | childParams.mRight = childParams.mLeft + childParams.width 581 | } 582 | } 583 | 584 | /// 585 | /// horizontal rules 586 | /// 587 | private func applyHorizontalSizeRules(params childParams: RelativeLayoutParams, width: CGFloat, 588 | rules: inout [BindViewWithRule]) { 589 | 590 | func hasBindView(rule: RelativeRule) -> Bool { 591 | let ruleValue: BindViewWithRule = rules[rule.getValue()] 592 | return ruleValue.VIEW != nil && ruleValue.TYPE == BindType.BIND 593 | } 594 | 595 | var anchorParams: RelativeLayoutParams? 596 | 597 | // min 598 | childParams.mLeft = RelativeLayoutParams.VALUE_NOT_SET 599 | childParams.mRight = RelativeLayoutParams.VALUE_NOT_SET 600 | 601 | // get the view left of current View 602 | anchorParams = getRelatedViewParams(rules: &rules, relation: .LEFT_OF) 603 | 604 | if anchorParams != nil { 605 | childParams.mRight = anchorParams!.mLeft 606 | - CGFloat(anchorParams!.leftMargin + anchorParams!.rightMargin) 607 | } else if childParams.alignWithParent && hasBindView(rule: .LEFT_OF) { 608 | if width >= 0 { 609 | childParams.mRight = width - CGFloat(childParams.rightMargin) 610 | } 611 | } 612 | 613 | anchorParams = getRelatedViewParams(rules: &rules, relation: .RIGHT_OF) 614 | 615 | if anchorParams != nil { 616 | childParams.mLeft = anchorParams!.mRight + CGFloat(anchorParams!.leftMargin + anchorParams!.rightMargin) 617 | } else if childParams.alignWithParent && hasBindView(rule: .RIGHT_OF) { 618 | childParams.mLeft = CGFloat(childParams.leftMargin) 619 | } 620 | 621 | anchorParams = getRelatedViewParams(rules: &rules, relation: .ALIGN_LEFT) 622 | if anchorParams != nil { 623 | childParams.mLeft = anchorParams!.mLeft + CGFloat(childParams.leftMargin) 624 | } else if childParams.alignWithParent && hasBindView(rule: .ALIGN_LEFT) { 625 | childParams.mLeft = CGFloat(childParams.leftMargin) 626 | } 627 | 628 | anchorParams = getRelatedViewParams(rules: &rules, relation: .ALIGN_RIGHT); 629 | if (anchorParams != nil) { 630 | childParams.mRight = anchorParams!.mRight - CGFloat(childParams.rightMargin) 631 | } else if childParams.alignWithParent && hasBindView(rule: .ALIGN_RIGHT) { 632 | if (width >= 0) { 633 | childParams.mRight = width - CGFloat(childParams.rightMargin) 634 | } 635 | } 636 | 637 | if hasBindView(rule: .ALIGN_PARENT_LEFT) { 638 | childParams.mLeft = CGFloat(childParams.leftMargin) 639 | } 640 | 641 | if hasBindView(rule: .ALIGN_PARENT_RIGHT) { 642 | if (width >= 0) { 643 | childParams.mRight = width - CGFloat(childParams.rightMargin) 644 | } 645 | } 646 | } 647 | 648 | 649 | private func getRelatedViewParams(rules: inout [BindViewWithRule], relation: RelativeRule) -> 650 | RelativeLayoutParams? { 651 | let view: UIView? = getRelatedView(rules: rules, relation: relation) 652 | 653 | if view != nil { 654 | let params: RelativeLayoutParams = view!.getLayoutParams() as! RelativeLayoutParams 655 | return params 656 | } 657 | 658 | return nil 659 | } 660 | 661 | private func getRelatedView(rules: [BindViewWithRule], relation: RelativeRule) -> UIView? { 662 | let view = rules[relation.getValue()].VIEW 663 | 664 | if view == nil { 665 | return view 666 | } 667 | 668 | let id = view!.getViewId() 669 | 670 | if id.viewId != 0 { 671 | var node: Node? = mDGraph.mKeyNodes.object(forKey: id) 672 | 673 | if node == nil { 674 | return nil 675 | } 676 | 677 | var view: UIView = (node?.view)! 678 | 679 | while view.isHidden { 680 | let l_rules: [BindViewWithRule] = (view.getLayoutParams() as! RelativeLayoutParams).getRules() 681 | node = mDGraph.mKeyNodes.object(forKey: l_rules[relation.getValue()].VIEW?.getViewId()) 682 | if node == nil { 683 | return nil 684 | } 685 | view = (node?.view)! 686 | } 687 | 688 | return view 689 | } 690 | 691 | return nil 692 | } 693 | 694 | private func sortChildren() { 695 | let graph = mDGraph 696 | 697 | for (_, child) in subviews.enumerated() { 698 | graph.add(view: child) 699 | } 700 | 701 | mSortedHorizontalChildren = graph.getSortedViews(rules: RelativeRule.RULES_HORIZONTAL) 702 | 703 | mSortedVerticalChildren = graph.getSortedViews(rules: RelativeRule.RULES_VERTICAL) 704 | } 705 | 706 | fileprivate class Node { 707 | var view: UIView! 708 | 709 | var dependents: NSMapTable = NSMapTable() 710 | 711 | var dependencies: NSMapTable = NSMapTable() 712 | 713 | static var mPool: ReleasePool = ReleasePool() 714 | 715 | init(view: UIView) { 716 | self.view = view 717 | } 718 | 719 | init() { 720 | // none init 721 | } 722 | 723 | static func acquire(view: UIView) -> Node { 724 | var node: Node? = mPool.acquire() 725 | if node == nil { 726 | node = Node(view: view) 727 | } else { 728 | node!.view = view 729 | } 730 | return node! 731 | } 732 | 733 | public func release() { 734 | self.view = nil 735 | dependents.removeAllObjects() 736 | dependencies.removeAllObjects() 737 | } 738 | 739 | public static func ==(_ a: Node, _ b: Node) -> Bool { 740 | if (a.view?.getViewId())! == (b.view?.getViewId())! { 741 | return true 742 | } 743 | return false 744 | } 745 | } 746 | 747 | fileprivate class DependencyGraph { 748 | 749 | public var mNodes: Array = Array() 750 | 751 | public var mKeyNodes: NSMapTable = NSMapTable() 752 | 753 | public var mRoots: Queue = Queue() 754 | 755 | public func clear() { 756 | var nodes = mNodes 757 | for item in nodes { 758 | item.release() 759 | } 760 | nodes.removeAll() 761 | mKeyNodes.removeAllObjects() 762 | mRoots.clear() 763 | } 764 | 765 | public func add(view: UIView) { 766 | let viewId = view.getViewId() 767 | let node = Node.acquire(view: view) 768 | 769 | if viewId.viewId != -1 { 770 | mKeyNodes.setObject(node, forKey: viewId) 771 | } 772 | 773 | mNodes.append(node) 774 | } 775 | 776 | public func getSortedViews(rules: [RelativeRule]) -> [UIView] { 777 | var roots: Queue = findRoots(rulesFilter: rules) 778 | var sorted: [UIView] = [] 779 | 780 | var node: Node? = roots.dequeue() 781 | 782 | while node != nil { 783 | let view: UIView = node!.view! 784 | let key: InnerInteger = view.getViewId() 785 | sorted.append(view) 786 | let dependents: NSMapTable = node!.dependents 787 | for dependent in dependents.keyEnumerator() { 788 | let node = dependent as! Node 789 | let dependencies = node.dependencies 790 | dependencies.removeObject(forKey: key) 791 | if dependencies.count == 0 { 792 | roots.enqueue(node) 793 | } 794 | } 795 | 796 | // update node 797 | node = roots.dequeue() 798 | } 799 | 800 | return sorted 801 | } 802 | 803 | public func findRoots(rulesFilter: [RelativeRule]) -> Queue { 804 | let keyNodes = mKeyNodes 805 | let nodes = mNodes 806 | 807 | // Find roots can be invoked several times, so make sure to clear 808 | // all dependents and dependencies before running the algorithm 809 | for item in nodes { 810 | item.dependents.removeAllObjects() 811 | item.dependencies.removeAllObjects() 812 | } 813 | 814 | // Builds up the dependents and dependencies for each node of the graph 815 | for node: Node in nodes { 816 | let layoutParams = node.view!.uiViewExtension.layoutParams as! RelativeLayoutParams 817 | 818 | let rules = layoutParams.rules 819 | 820 | let ruleCount = rulesFilter.count 821 | 822 | for index in 0 ..< ruleCount { 823 | let rule: BindViewWithRule = rules[rulesFilter[index].getValue()] 824 | 825 | if rule.RULE.getValue() >= 0 { 826 | let dependency = keyNodes.object(forKey: rule.VIEW?.getViewId()) 827 | 828 | if dependency == nil || dependency! == node { 829 | continue 830 | } 831 | 832 | dependency?.dependents.setObject(self, forKey: node) 833 | node.dependencies.setObject(dependency, forKey: rule.VIEW?.getViewId()) 834 | } 835 | } 836 | } 837 | 838 | var roots: Queue = mRoots 839 | roots.clear() 840 | 841 | for node: Node in nodes { 842 | if node.dependencies.count == 0 { 843 | roots.enqueue(node) 844 | } 845 | } 846 | 847 | return roots 848 | } 849 | } 850 | 851 | public func addView(view: UIView, params: RelativeLayoutParams = RelativeLayoutParams.generateDefaultParams()) { 852 | view.uiViewExtension.layoutParams = params 853 | 854 | if view.superview != nil { 855 | return 856 | } 857 | 858 | if view is JustViewGroup { 859 | (view as! JustViewGroup).setParent(viewGroup: self) 860 | } 861 | 862 | addSubview(view) 863 | 864 | // bind view with params 865 | view.setViewId(id: UIView.generateViewId()) 866 | params.bindWith(view: view) 867 | } 868 | 869 | override public func addView(view: UIView) { 870 | super.addView(view: view) 871 | self.addView(view: view, params: RelativeLayoutParams.generateDefaultParams()) 872 | } 873 | } 874 | --------------------------------------------------------------------------------