Skip to content

Commit

Permalink
Merge pull request #21 from xzeror/master
Browse files Browse the repository at this point in the history
Swift 5, SwiftLint, Refactoring
  • Loading branch information
s4cha authored Apr 29, 2019
2 parents 88a69ce + 5a8ea44 commit 82adb80
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 66 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,6 @@ fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output

# macos
.DS_Store
115 changes: 115 additions & 0 deletions KeyboardLayoutGuide/.swiftlint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
excluded: # paths to ignore during linting. Takes precedence over `included`.
- Carthage
- Pods

disabled_rules: # rule identifiers to exclude from running
- block_based_kvo # Swift implementatoin has bugs which lead to a crash - https://bugs.swift.org/browse/SR-5116, https://bugs.swift.org/browse/SR-5117
- trailing_comma # There should be a com after the last item in the list

opt_in_rules: # some rules are only opt-in
- attributes
- anyobject_protocol
- discouraged_optional_boolean
- closure_body_length
- closure_end_indentation
- closure_spacing
- collection_alignment
- contains_over_first_not_nil
- convenience_type
- discouraged_object_literal
- discouraged_optional_collection
- empty_count
- empty_string
- empty_xctest_method
- explicit_init
- extension_access_modifier
- fallthrough
- fatal_error_message
- first_where
# - file_header
- file_name
- force_unwrapping
- function_default_parameter_at_end
- identical_operands
- implicit_return
- implicitly_unwrapped_optional
- joined_default_parameter
- last_where
- legacy_random
- let_var_whitespace
- literal_expression_end_indentation
- lower_acl_than_parent
- missing_docs
- modifier_order
- multiline_arguments
- multiline_arguments_brackets
- multiline_literal_brackets
- multiline_parameters
- multiline_parameters_brackets
- nimble_operator
- number_separator
- operator_usage_whitespace
- override_in_extension
- overridden_super_call
- pattern_matching_keywords
- prefixed_toplevel_constant
- private_action
- private_outlet
- prohibited_interface_builder
- prohibited_super_call
- quick_discouraged_call
- redundant_nil_coalescing
- redundant_type_annotation
- required_enum_case
- single_test_class
- sorted_first_last
- sorted_imports
- static_operator
- strict_fileprivate
- switch_case_on_newline
- toggle_bool
- trailing_closure
- unavailable_function
- unneeded_parentheses_in_closure_argument
- untyped_error_in_catch
- unused_import
- unused_private_declaration
- vertical_parameter_alignment_on_call
- vertical_whitespace_closing_braces
- vertical_whitespace_opening_braces
- xct_specific_matcher
- yoda_condition

line_length: 200

identifier_name:
severity: warning
min_length: 3
max_length: 40
excluded:
- id
- ID
- rx
validates_start_with_lowercase: true

function_body_length: 50

cyclomatic_complexity:
warning: 8
error: 10

force_unwrapping:
severity: error

redundant_type_annotation:
severity: error

custom_rules:
empty_lines_after_type_declarations:
included: ".*.swift"
name: "Empty lines after type declarations"
regex: '(struct|class|enum|protocol|extension) ([\w]+(:\s*[\w\s,]+)* )\{\n\n'
message: "There should be no empty lines after type declarations"
severity: error

warning_threshold: 1
22 changes: 22 additions & 0 deletions KeyboardLayoutGuide/KeyboardLayoutGuide.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
9902DE071FBB24A5009E0D48 /* Frameworks */,
9902DE081FBB24A5009E0D48 /* Headers */,
9902DE091FBB24A5009E0D48 /* Resources */,
10B2D35122645E990028548F /* SwiftLint */,
);
buildRules = (
);
Expand Down Expand Up @@ -197,6 +198,27 @@
};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
10B2D35122645E990028548F /* SwiftLint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = SwiftLint;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "if which swiftlint >/dev/null; then\n/usr/local/bin/swiftlint autocorrect --quiet\n/usr/local/bin/swiftlint lint --reporter \"xcode\" --quiet \nelse\necho \"error: SwiftLint not installed, 'brew install swiftlint' or download from https://github.com/realm/SwiftLint\"\nexit -1\nfi\n";
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
9902DE061FBB24A5009E0D48 /* Sources */ = {
isa = PBXSourcesBuildPhase;
Expand Down
91 changes: 41 additions & 50 deletions KeyboardLayoutGuide/KeyboardLayoutGuide/Keyboard+LayoutGuide.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,54 +13,51 @@ private class Keyboard {
var currentHeight: CGFloat = 0
}

public extension UIView {

private struct AssociatedKeys {
extension UIView {
private enum AssociatedKeys {
static var keyboardLayoutGuide = "keyboardLayoutGuide"
}

/// A layout guide representing the inset for the keyboard.
/// Use this layout guide’s top anchor to create constraints pinning to the top of the keyboard.
var keyboardLayoutGuide: KeyboardLayoutGuide {
get {
if let obj = objc_getAssociatedObject(self, &AssociatedKeys.keyboardLayoutGuide) as? KeyboardLayoutGuide {
return obj
}
let new = KeyboardLayoutGuide()
addLayoutGuide(new)
new.setUp()
objc_setAssociatedObject(self, &AssociatedKeys.keyboardLayoutGuide, new as Any, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return new
public var keyboardLayoutGuide: KeyboardLayoutGuide {
if let obj = objc_getAssociatedObject(self, &AssociatedKeys.keyboardLayoutGuide) as? KeyboardLayoutGuide {
return obj
}
let new = KeyboardLayoutGuide()
addLayoutGuide(new)
new.setUp()
objc_setAssociatedObject(self, &AssociatedKeys.keyboardLayoutGuide, new as Any, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return new
}
}

open class KeyboardLayoutGuide: UILayoutGuide {

@available(*, unavailable)
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override init() {

public init(notificationCenter: NotificationCenter = NotificationCenter.default) {
super.init()

// Observe keyboardWillChangeFrame notifications
let nc = NotificationCenter.default
nc.addObserver(self,
selector: #selector(keyboardWillChangeFrame(_:)),
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil)
notificationCenter.addObserver(
self,
selector: #selector(keyboardWillChangeFrame(_:)),
name: UIResponder.keyboardWillChangeFrameNotification,
object: nil
)
}

internal func setUp() {
guard let view = owningView else {
return
}
NSLayoutConstraint.activate([
heightAnchor.constraint(equalToConstant: Keyboard.shared.currentHeight),
leftAnchor.constraint(equalTo: view.leftAnchor),
rightAnchor.constraint(equalTo: view.rightAnchor),
])
guard let view = owningView else { return }
NSLayoutConstraint.activate(
[
heightAnchor.constraint(equalToConstant: Keyboard.shared.currentHeight),
leftAnchor.constraint(equalTo: view.leftAnchor),
rightAnchor.constraint(equalTo: view.rightAnchor),
]
)
let viewBottomAnchor: NSLayoutYAxisAnchor
if #available(iOS 11.0, *) {
viewBottomAnchor = view.safeAreaLayoutGuide.bottomAnchor
Expand All @@ -69,57 +66,52 @@ open class KeyboardLayoutGuide: UILayoutGuide {
}
bottomAnchor.constraint(equalTo: viewBottomAnchor).isActive = true
}

@objc
private func keyboardWillChangeFrame(_ note: Notification) {
if var height = note.keyboardHeight {
if #available(iOS 11.0, *), height > 0 {
height -= (owningView?.safeAreaInsets.bottom)!
if #available(iOS 11.0, *), height > 0, let bottom = owningView?.safeAreaInsets.bottom {
height -= bottom
}
heightConstraint?.constant = height
animate(note)
Keyboard.shared.currentHeight = height
}
}

private func animate(_ note: Notification) {
if isVisible(view: self.owningView!) {
if
let owningView = self.owningView,
isVisible(view: owningView)
{
self.owningView?.layoutIfNeeded()
} else {
UIView.performWithoutAnimation {
self.owningView?.layoutIfNeeded()
}
}
}

deinit {
NotificationCenter.default.removeObserver(self)
}
}

// MARK: - Helpers

extension UILayoutGuide {
internal var heightConstraint: NSLayoutConstraint? {
guard let target = owningView else { return nil }
for c in target.constraints {
if let fi = c.firstItem as? UILayoutGuide, fi == self && c.firstAttribute == .height {
return c
}
return owningView?.constraints.first {
$0 == self && $0.firstAttribute == .height
}
return nil
}
}

extension Notification {
var keyboardHeight: CGFloat? {
guard let v = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
guard let keyboardFrame = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
return nil
}
// Weirdly enough UIKeyboardFrameEndUserInfoKey doesn't have the same behaviour
// in ios 10 or iOS 11 so we can't rely on v.cgRectValue.width
let screenHeight = UIApplication.shared.keyWindow?.bounds.height ?? UIScreen.main.bounds.height
return screenHeight - v.cgRectValue.minY
return screenHeight - keyboardFrame.cgRectValue.minY
}
}

Expand All @@ -136,4 +128,3 @@ func isVisible(view: UIView) -> Bool {
}
return isVisible(view: view, inView: view.superview)
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,17 @@
// Copyright © 2017 freshos. All rights reserved.
//

import XCTest
@testable import KeyboardLayoutGuide
import XCTest

class KeyboardLayoutGuideTests: XCTestCase {

override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}

override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}

func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}

func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}

}

0 comments on commit 82adb80

Please sign in to comment.