- vim ~/.zshrc
eval "$(rbenv init - zsh)"
alias neo="cd Documents/GitHub/[project]"
alias podinstall="bundle exec pod install --repo-update"
alias chore="./chore.bash"
alias sshadd="ssh-add -K ~/.ssh/[user-project]-GitHub"
- source ~/.zshrc
iOS Developer
eval "$(rbenv init - zsh)"
alias neo="cd Documents/GitHub/[project]"
alias podinstall="bundle exec pod install --repo-update"
alias chore="./chore.bash"
alias sshadd="ssh-add -K ~/.ssh/[user-project]-GitHub"
Use InitialState in setUp
Don’t use currentState if the code is not prepared to any change after Setup
Or you can use subscribe with take(1)
https://github.com/marketplace/actions/auto-assign-action
name: auto-assign
on:
pull_request:
types: [opened]
jobs:
add-reviews:
runs-on: ubuntu-latest
steps:
- uses: kentaro-m/auto-assign-action@v1.2.3
addAssignees: author
let animationView = AnimationView(name: "Test")
animationView.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
animationView.center = self.view.center
animationView.contentMode = .scaleAspectFill
view.addSubview(animationView)
animationView.play()
let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(100),
heightDimension: .absolute(100))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 2, leading: 2, bottom: 2, trailing: 2)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
heightDimension: .absolute(100))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
group.interItemSpacing = .flexible(16)
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 20)
section.interGroupSpacing = 16
The key is flexible interItemSpacing. But interGroupSpacing can be different from interItemSpacing.
let sub = NotificationCenter.default
.publisher(for: NSControl.textDidChangeNotification, object: filterField)
.map( { ($0.object as! NSTextField).stringValue } )
.assign(to: \MyViewModel.filterString, on: myViewModel)
요약하자면 MVVM의 핵심은
그럼 뭔가를 보여줄 일이 생긴다면
유저 액션이 발생했다면?
리액티브한 ViewModel을
결론
MVVM은 결국 MVC와 다른건 뷰컨트롤러에서 모델을 직접 접근해서 많은 일들을 처리했다면 그런 로직들을뷰모델에 옮긴다는데에 의미가 있는 것 같다. 사용자 인터페이스(뷰)의 개발을 비즈니스 로직 또는 백-엔드 로직(모델)로부터 분리시켜서 뷰가 어느 특정한 모델 플랫폼에 종속되지 않도록 해준다. 결국 MVC와의 차이는 “Presentation 로직”을 ViewController 코드에서 ViewModel 코드로 옮긴다는데에 있다.
PDF files are just used by Xcode at COMPILE time. Xcode will generate the PNG files and PNGs is what your binary will contain.
https://bjango.com/articles/idontusepdfs/
-> pdf image is exported with scale 1 standard. So it has lower quality than having png 2x and 3x. And also even though asset is pdf, Xcode generates png files anyway, and the size of png files made by Xcode is much bigger than using png files at first.
I wanted to see whether there is real quality difference between images from png and pdf. First one is png, and the second one is pdf. I could see the real difference. So my conclusion is to use png file with individual scales if pdf format is not needed.
prerequisite: animationController(forDismissed:)
//MARK: - Interactive Transition
private var dismissInteractiveTransition = InteracitveTransition()
override func viewDidLoad() {
super.viewDidLoad()
addPanGestureRecognizer()
}
extension ChromecastExpandedViewController {
override func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return dismissInteractiveTransition.hasStarted ? dismissInteractiveTransition : nil
}
private func addPanGestureRecognizer() {
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(didPan(sender:)))
panGestureRecognizer.maximumNumberOfTouches = 1
view.addGestureRecognizer(panGestureRecognizer)
}
@objc private func didPan(sender: UIPanGestureRecognizer) {
let progressThreshold:CGFloat = 0.3
let progress = dismissProgress(of: sender)
switch sender.state {
case .began:
dismissInteractiveTransition.hasStarted = true
dismiss(animated: true, completion: nil)
case .changed:
dismissInteractiveTransition.shouldFinish = progress > progressThreshold
dismissInteractiveTransition.update(progress)
case .cancelled:
dismissInteractiveTransition.hasStarted = false
dismissInteractiveTransition.cancel()
case .ended:
dismissInteractiveTransition.hasStarted = false
dismissInteractiveTransition.shouldFinish
? dismissInteractiveTransition.finish()
: dismissInteractiveTransition.cancel()
default:
break
}
}
private func dismissProgress(of gestureRecognizer:UIPanGestureRecognizer) -> CGFloat {
let touchPoint = gestureRecognizer.translation(in: gestureRecognizer.view)
var downwardMovement = Float(touchPoint.y / view.bounds.height)
downwardMovement = fmaxf(downwardMovement, 0.0)
downwardMovement = fminf(downwardMovement, 1.0)
let progress = CGFloat(downwardMovement)
return progress
}
}
class InteracitveTransition: UIPercentDrivenInteractiveTransition {
var hasStarted = false
var shouldFinish = false
}