
싱가포르 아마존에서 저렴하게 구매한 JETech 슬리브
이전에 사용하던 맥북 케이스는 튤레 제품 15인치 전용이었다. 2015년에 구매해서 8년을 사용했다.


JETech 맥북 14인치 슬리브





패브릭 재질의 슬리브이다. 중국산인거 같은데 가격이 저렴해서 구매했다. 생각보다 퀄리티가 괜찮았고 마감처리도 마음에 들었다.



싱가포르 아마존에서 저렴하게 구매한 JETech 슬리브
이전에 사용하던 맥북 케이스는 튤레 제품 15인치 전용이었다. 2015년에 구매해서 8년을 사용했다.







패브릭 재질의 슬리브이다. 중국산인거 같은데 가격이 저렴해서 구매했다. 생각보다 퀄리티가 괜찮았고 마감처리도 마음에 들었다.


코로나로 인하여 아시아나 항공 승급기준이 2년 -> 4년으로 연장되었다.
즉, 4년이내에 탑승 마일리지와 제휴 마일리지를 쌓으면 승급이 된다.
공항에 가면 체크인 하는 곳 왼쪽에 회원등급 관련 카운터가 있다. 여기 직원한테 문의하면 아래 사진과 같이 친절히 적어준다.

만년 골드등급에서 다이아몬드로 넘어가기 위한 조건은 다음과 같다.
총 4만 마일리지를 적립해야 되는데 아래와 같은 조건으로 적립해야 된다.
탑승마일리지 3만 마일리지
제휴 마일리지 1만 마일리지

편도기준 적립표는 위의 표를 참고 바란다.
싱가포르 왕복 비행기를 타면 5768 마일리지가 적립된다.
3만 마일리지를 탑승 마일리지로 채우려면 6번 왕복하면 된다.
이건 그냥 신용카드중에 카드결제시 마일리지 적립해주는 카드를 사용하면 채울 수 있다. 보통 카드사별로 조건이 다르지만 메인 신용카드를 마일리지카드로 쓴다면 1만 마일리지를 4년이라는 시간에 채우는 것은 크게 어렵지 않다.
비즈니스클래스 입장시 탑승 가능.
수화물 골드보다 빨리 나옴.
수화물 2개까지 무료. (단 추가 수화물은 20kg까지)
우수회원 탑승 보너스 마일리지 10% 추가 적립
위의 4가지가 가장 큰 혜택이다


In this post, I’ll share sample code for implementing MasonryLayout using UICollectionViewCompositionalLayout.
I defined a very simple UICollectionViewCell. It consists of basic UI components such as Photo, Title and Description.

This is important to implement masonryLayout. Unfortunately It doesn’t support NSCollectionLayoutSize. (It supports GCRect only)
It means we need to calculate the cell’s contents size.
So We need to write 2 main logics.
To calculate size, We need to know cell’s width first.
We knows collectionView’s width by accessing UICollectionViewCompositionalLayout’s layoutEnvironment.container.contentSize.
private func masonryLayout(contentInset: NSDirectionalEdgeInsets) -> UICollectionViewCompositionalLayout {
let layout = UICollectionViewCompositionalLayout { [weak self] sectionIndex, layoutEnvironment in
var items = [NSCollectionLayoutGroupCustomItem]()
let snapshotItems = self?.dataSource.snapshot(for: .item).items
let numberOfItems = snapshotItems?.count ?? 0
let contentSize = layoutEnvironment.container.contentSize
...
}
We have 2 columns. so Cell’s width is contentSize.width / columns.
If we set interItemSpacing and NSDirectionalEdgeInsets then logic is below
let horizontalSpacing = contentInsets.leading + contentInsets.trailing
let spacing = (columns - 1) * interItemSpacing + horizontalSpacing
return (contentSize.width - spacing) / columns
Calculate UIImageView’s size
In this example, I set image ratio as 4:3. So Image Height is cell’s width * 0.75.
Calculate UILabel’s size
To get the correct size, We need to provide the details such as Font, LineBreakMode and NumberOfLines.
Also to get the correct bounding size we set NSString.DrawingOptions.
If you want to know more details about drawing options visit Apple documents.
extension UILabel {
var textHeight: CGFloat? {
guard let labelText = text else {
return nil
}
let attributes: [NSAttributedString.Key: UIFont] = [
.font: font
]
let labelTextSize = (labelText as NSString).boundingRect(
with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude),
options: .usesLineFragmentOrigin,
attributes: attributes,
context: nil
).size
return ceil(labelTextSize.height)
}
}
func height(text:String, font:UIFont, numberOfLines: CGFloat, lineBreakMode: NSLineBreakMode, width:CGFloat) -> CGFloat{
let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: UILabel.noIntrinsicMetric))
label.numberOfLines = numberOfLines
label.lineBreakMode = lineBreakMode
label.font = font
label.text = text
return label.textHeight ?? 0
}

We need to layout cell’s position in a Group. CGRect has origin x, y and width, height. At step 1, We already calculated the cell’s width and height. In step 2, We need to calculate origins.
Calculate cell’s origin X and Y

Let’s take a look how to layout cells. (row is indicating item’s index)
First Item (row: 0, column: 0)
It’s origin is 0, 0
Second Item (row: 1, column: 1)
It’s origin y is 0. But x is first item’s width + interItemSpacing.
let originX = (cellWidth + interItemSpacing) * columnIndex
Third Item (row: 2, column: 0)
Its origin x is zero because columnIndex is zero. But origin y is calculated by maxY between First Item and Second Item. Second Item’s maxY is smaller than First Item. So We picked this to layout Third Item.
Here is full source code.
cachedSize used for saving cell’s height. It will be called when you call a reloadData or applySnapshotUsingReloadData. (In this sample, I used dictionary but you can considering NSCache)
private func masonryLayout(contentInset: NSDirectionalEdgeInsets) -> UICollectionViewCompositionalLayout {
let layout = UICollectionViewCompositionalLayout { [weak self] sectionIndex, layoutEnvironment in
var items = [NSCollectionLayoutGroupCustomItem]()
let snapshotItems = self?.dataSource.snapshot(for: .item).items
let numberOfItems = snapshotItems?.count ?? 0
let contentSize = layoutEnvironment.container.contentSize
let itemProvider = MasonryLayoutProvider(
columns: 2,
interItemSpacing: 16,
collectionWidth: contentSize.width,
contentInsets: contentInset) { [weak self] row, cellWidth in
//Use Cached Height
if let cachedHeight = self?.cachedSize[row]?.height {
return cachedHeight
}
let height = snapshotItems?[row].estimatedHeight(width: cellWidth) ?? 0
self?.cachedSize[row] = .init(width: cellWidth, height: height)
return height
}
for i in 0..<numberOfItems {
let item = itemProvider.makeLayoutItem(for: i)
items.append(item)
}
let groupLayoutSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .absolute(itemProvider.maxColumnHeight())
)
let group = NSCollectionLayoutGroup.custom(layoutSize: groupLayoutSize) { env in
items
}
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = contentInset
section.boundarySupplementaryItems = [
.init(layoutSize: .init(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(10)), elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
]
return section
}
return layout
}
For more details, You can checkout my repository
https://www.kodeco.com/4829472-uicollectionview-custom-layout-tutorial-pinterest
https://github.com/eeshishko/WaterfallTrueCompositionalLayout

싱가포르는 생각보다 모기는 없는 편이다. 그래서인지 방충망 없이 사는 집들이 대부분이다. 하지만 여긴 개미랑 도마뱀이 존재한다.
아무리 높은 층을 살아도 신기할 정도로 도마뱀이 보인다. 하루종일 걱정없이 창문을 열고 싶다면 방충망 설치를 추천한다.
생각보다 어렵지 않다. 이케아 가구 조립보다 살짝 시간이 걸린다는 정도?
일단 나는 라자다에서 방충망을 구매했다. 대충 마그넷 메시 어쩌구로 검색하니 여러제품들이 나왔다.
우리집은 2 베드룸인데 여기에 창문이 총 4개다. 대충 사이즈를 넉넉하게 측정 후 4개를 구매했다. 총 비용은 120불 정도 들었다.
구성품을 보면 본드, 줄자, 방충망 (폴리에스테르 같은 재질임), 자석 그리고 니퍼랑 커터칼 까지 포함되어있다.
설명서에도 잘 나와있지만 다음과 같이 진행하면 된다.
1. 일단 자석을 창틀에 붙인다.
2. 창틀에 붙인 자석에 맞닿게 다른 극의 자석을 붙인다.
3. 자석에 붙어있는 양면 테이프 떼어낸다.
4. 자석의 길이에 맞게 방충망을 댄다.
5. 방충망을 댄 후 자석부분에 본드를 바른다.
6. 본드 바른 부분에 긴 플라스틱을 창틀 길이에 맞게 잘라내면서 붙인다. (이 부분이 그나마 좀 난이도 높음)
7. 모서리 부분은 ㄱ자로 생긴 플라스틱 연결부위로 잘 연결하자.
8. 삐죽 튀어나언 방충망을 칼로 정리한다 (끝!)







방충망이 설치된 모습
좀 고생을 했지만 만족도가 높다. 금액도 저렴한 편이고 하루종일 창문 열고 생활해도 벌레하나 없다. 도마뱀도 우리집에서는 1년반 넘도록 한번도 못봤다.
마음 같아서는 발코니부분에도 방충망을 설치하고 싶다

여행갈 때 내가 어디서 얼마를 썼는지 기록하는 유용한 앱이다.
이 앱의 가장 큰 장점
– 무료앱
– 현재 위치기반 날씨 정보 제공
– 하루에 얼마나 걸었는지 걸음수 제공
– 현재 위치기반 환율 정보 제공 (금액 입력하면 알아서 원화로 표시)
– 경비 예산 설정
– 예산 대비 얼마나 지출했는지 그래프로 제공





먹고 쓰고 기록하는 앱
광고아님. 직접 개발함. 필요한 기능이나 불편사항 있으면 코멘트 남겨주세요

오늘은 Jalan Kayu 지역에서 가장 유명한 플라타 맛집을 다녀왔다. 싱가포르 현지인들이라면 Jalan Kayu가 플라타로 유명하다는 것을 안다.



오리지널 플라타라고 쓰여있다. 바로 왼쪽에는 새우국수로 유명한 블랑코 새우국수집도 있다.
굉장히 다양한 플라타 메뉴들이 있다. 일단 이 집에서 유명한 메뉴는 에그 플라타, 치즈 플라카, 바나나 플라타 등이 있다.
플라타를 주문하면 기본 소스로 살짝 매콤한 커리가 나오는데 찍어먹으면 플라타의 맛과 잘 어울린다.
이곳은 티로도 유명한데 Teh라는 티를 판다.
Teh는 일반적인 블랙티이고 Teh-O는 우유가 들어간 블랙티이다.


여기 구글 후기를 보면 약간 안좋은 평도 있다. 주로 너무 기름지게 나왔다 등의 평인데 최근에는 이런 후기를 의식했는지 플라타를 아주 겉은 바삭하게 속은 촉촉하게 바로 갓구어서 잘 서빙해준다.
내 생각엔 사람들이 많이 방문하는 주말 아침 즈런치 타임에 가면 최상의 플라타를 먹을 수 있을 것 같다. 주문이 많이 들어오기에 바로바로 구워서 준다.
재방문 의사 100프로인 식당이다

오늘 오차드가서 아이폰 15 프로를 샀다.
그동안 아이폰 13 미니 256기가를 써왔는데 완충을 해도 반나절도 잘 못 버티는 배터리에 질렸다.
왠만하면 폰을 4년정도는 쓰는데 아이폰 미니 13은 이재 그만 쓰기로 결정.
싱가포르에서는 무이자 24개월 할부로 구매가 가능하다. 온라인으로 채팅 연결을 통해야지만 24개월 할부 결제가 가능하고 아니면 애플 스토어 직접 방문하는 방법이 있다.
나는 색상도 직접 확인 할 겸해서 방문했다.




투명 케이스랑 아이폰 15 프로 1테라 구매했다




아이폰 15 프로 언박싱 후 느낀점. 오 정말 가볍다.
느낌상 아이폰 미니랑 별 차이가 없게 느껴져거 설마 하는 마음에 무게를 재봤다. 둘다 액정보호필름 뗀 상태로 측정함.


역시 아이폰 15 프로가 당연히 더 무겁긴 했다.
영혼까지 백업하려면 폰에서 폰으로 하라는 이야기가 있길래 그렇게 진행했다. (영혼까지 백업 안됨)



너무 만족스럽다. 특히 배터리

싱가포르 항공의 저가 브랜드인 스쿠트 항공은 KrisFlyer 마일리지로도 항공권을 구매할 수 있다. 싱가포르 항공을 타든 스쿠트를 타든 KrisFlyer 마일리지로 적립된다.
스쿠트항공 앱도 있지만 앱은 결제 방법에 KrisFlyer 마일리지 결제 옵션이 아에 없다. 반드시 웹 사이트인 https://www.flyscoot.com/en 들어가서 결제하자.

현재 19740 마일리지는 싱가포르 달러로 환산하면 419.76달러 (약 42만원 정도) 된다.
항공권 구매 가격에서 마일리지를 사용하면 저 금액만큼 제외되고 나머지 금액만 신용카드로 결제가 가능하다.
스쿠트 항공은 웹사이트랑 앱이랑 너무 다르다. 예를 들어 웹 체크인이 웹사이트에서는 되는데 앱에서는 안된다던가 하는 일이 빈번하게 발생한다. 스쿠트 항공을 이용한다면 웹 체크인은 필수다. 그리고 마일리지가 있다면 반드시 웹 사이트에 들어가야 사용할 수 있다. 언젠가는 앱도 지원해주겠지….

미슐랭 맛집인 벱메인을 다녀왔다.
https://maps.app.goo.gl/jqaYnBboWHnGEf4D8
호치민에 와서 며칠 연속 쌀국수만 먹다가 좀 더 다른 메뉴를 먹고 싶어서 들린 곳. 결론적으로는 2박 3일 방문했던 모든 음식점중에서 제일 맛있었다.









우리가 시킨 메뉴들.
호치민에 들린 다면 꼭 방문해야 될 강추 맛집이다.
모든 메뉴가 다 맛있었지만 특히 맛있었던 메뉴는 오므라이스처럼 생긴 3번째 메뉴와 피자처럼 생긴 첫 번째 메뉴 그리고 샌드위치 처럼 생긴 메뉴 모두 정말 맛있었다.

여기는 Punggol 지역 Oasis LRT역 근처에 있는 카페다.
시간이 조금 남아서 잠깐 들린 곳.
분위기도 좋고 커피도 맛있었다. 이 근처에 살거나 올일이 있다면 추천할 만한 곳이다. 재방문 의사 100%





You must be logged in to post a comment.